定制 pinvandaag/my-buckaroo-api-php 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

pinvandaag/my-buckaroo-api-php

最新稳定版本:v1.1.11

Composer 安装命令:

composer require pinvandaag/my-buckaroo-api-php

包简介

A PHP wrapper for the My Buckaroo API

README 文档

README

A PHP Wrapper for the My Buckaroo API

Installation

composer require pinvandaag/my-buckaroo-api-php

Small usage example

<?php

use PinVandaag\BuckarooAPI\BuckarooAPIClient;

final class BuckarooController
{
    private BuckarooAPIClient $apiClient;

    public function __construct()
    {
        $this->apiClient = (new BuckarooAPIClient())
            ->configure(
                clientId: 'your-client-id',
                clientSecret: 'your-client-secret',
                baseUri: 'https://api.buckaroo.io'
            );
    }

    public function getAccessToken()
    {
        $token = $this->apiClient->getAccessToken();

        $authHeader = $token->authorizationHeader();

        print_r($token);
        print_r($authHeader);
        print_r($token->accessToken);
    }
}

Example urls

// http://example.com/buckaroo/accounts
// http://example.com/buckaroo/accounts/account?id=acc_abcd1234efgh5678
// http://example.com/buckaroo/accounts/update-payout-settings?id=acc_abcd1234efgh5678&payoutIban=NL09BNPA0880000090&grouping=Merchant&payoutInterval=Weekly&payoutDays=1,2,3
// http://example.com/buckaroo/apikeys
// http://example.com/buckaroo/apikeys?continuationToken=YOUR_NEXT_TOKEN
// http://example.com/buckaroo/apikey?id=apk_abcd1234efgh5678
// http://example.com/buckaroo/apikey/delete?id=apk_abcd1234efgh5678
// http://example.com/buckaroo/apikey/update?id=apk_abcd1234efgh5678&scopes=sale:save%20sale:read
// http://example.com/buckaroo/apikey/decrypt?id=apk_abcd1234efgh5678
// http://example.com/buckaroo/applications/search
// http://example.com/buckaroo/applications/search?name=My%20Application
// http://example.com/buckaroo/applications?id=app_1234abcd
// http://example.com/buckaroo/applications/create?name=My%20Application&applicationType=Merchant&scopes=sale:save%20sale:read
// http://example.com/buckaroo/applications/create?name=Webhook%20App&applicationType=Merchant&scopes=sale:read%20sale:save&webhookUrl=https://example.com/webhooks&webhookSecret=testsecret&eventTypes=SALE:READ,SALE:SAVE
// http://example.com/buckaroo/applications/installations/search?id=app_1234abcd
// http://example.com/buckaroo/applications/installations/search?id=app_1234abcd&merchantId=m_1234567890
// http://example.com/buckaroo/applications/installations/search?id=app_1234abcd&status=Enabled
// http://example.com/buckaroo/applications/installations?id=app_1234abcd&installationId=ins_1234abcd
// http://example.com/buckaroo/search_customers?email=somebody@example.com
// http://aanmelden.pinvandaag.local/client/dashboard/buckaroo/create_or_update_customer?customerReference=12345&givenName=Some&familyName=Body&street=Nimrodstraat&houseNumber=15&city=Tilburg
// http://example.com/buckaroo/search_customers?customerReference=12345
// http://example.com/buckaroo/customer?id=cus_abcd1234efgh5678
// http://example.com/buckaroo/customer/delete?id=cus_abcd1234efgh5678
// http://example.com/buckaroo/installations/search
// http://example.com/buckaroo/installations/search?status=Enabled
// http://example.com/buckaroo/installations/search?status=Disabled
// http://example.com/buckaroo/installations?id=ins_1234abcd
// http://example.com/buckaroo/installations/update?id=ins_1234abcd&status=Enabled
// http://example.com/buckaroo/installations/update?id=ins_1234abcd&status=Disabled&scopes=sale:read%20sale:save&eventCodes=SALE:READ,SALE:SAVE
// http://example.com/buckaroo/invoices?id=inv_43Ez3GCllu4Ss7Qo
// http://example.com/buckaroo/invoices/attachment?id=att_43HfY8gmNzSVzZhq
// http://example.com/buckaroo/invoices/creditnote?id=creditnote_id_here
// http://example.com/buckaroo/invoices/search
// http://example.com/buckaroo/invoices/search?currency=EUR
// http://example.com/buckaroo/invoices/search?paymentStatus=Open
// http://example.com/buckaroo/invoices/search?invoiceNumber=BUCKEUR0000000001
// http://example.com/buckaroo/invoices/search?fromInvoiceDate=2024-05-01T00:00:00%2B02:00&toInvoiceDate=2024-05-31T23:59:59%2B02:00
// http://example.com/buckaroo/merchant
// http://example.com/buckaroo/merchant/update?defaultLanguage=nl-NL
// http://example.com/buckaroo/merchant/features?continuationToken=TOKEN_FROM_PREVIOUS_RESPONSE
// http://example.com/buckaroo/merchant/legalentity
// http://example.com/buckaroo/oauth/convert?merchantId=m_1234567890&zitadelToken=YOUR_ZITADEL_TOKEN
// http://example.com/buckaroo/oauth/convert?merchant_id=m_1234567890&zitadel_token=YOUR_ZITADEL_TOKEN
// http://example.com/buckaroo/paymentmethods/subscriptions/search
// http://example.com/buckaroo/paymentmethods/subscriptions/search?paymentMethodCode=ideal
// http://example.com/buckaroo/paymentmethods/subscriptions/search?currency=EUR&status=Enabled
// http://example.com/buckaroo/paymentmethods/subscriptions?id=svs_1nASC78SMNYZ6Oqi
// http://example.com/buckaroo/paymentmethods/subscriptions/update?id=svs_1nASC78SMNYZ6Oqi&action=Activate
// http://example.com/buckaroo/paymentmethods/subscriptions/update?id=svs_1nASC78SMNYZ6Oqi&action=Deactivate
// http://example.com/buckaroo/paymentmethods/subscriptions/update?id=svs_1nASC78SMNYZ6Oqi&action=Update&currencies=EUR,USD
// http://example.com/buckaroo/paymentmethods/subscriptions/reprioritise?code=Shield&orderedSubscriptionIds=svs_first,svs_second,svs_third
// http://example.com/buckaroo/payouts/search
// http://example.com/buckaroo/payouts/search?currency=EUR
// http://example.com/buckaroo/payouts/search?status=Processed
// http://example.com/buckaroo/payouts/search?fromDate=2024-05-01T00:00:00%2B02:00&toDate=2024-05-31T23:59:59%2B02:00
// http://example.com/buckaroo/payouts?id=pay_abcd1234efgh5678
// http://example.com/buckaroo/terminals/search
// http://example.com/buckaroo/terminals/search?storeId=s_46R1G8q4
// http://example.com/buckaroo/terminals/search?status=Enabled
// http://example.com/buckaroo/terminals/search?storeId=s_46R1G8q4&status=Enabled
// http://example.com/buckaroo/terminals/smart?terminalId=t_abcd1234efgh5678
// http://example.com/buckaroo/terminals/smart/update?terminalId=t_46j1I7ul38rKdyub&name=Updated%20Terminal&terminalMode=Both&refundEnabled=true&forceConsumerPin=true&customerReceiptMode=Enabled&merchantReceiptMode=Enabled
// http://example.com/buckaroo/terminals/smart/cancel?terminalId=t_46j1I7ul38rKdyub
// http://example.com/buckaroo/terminals/smart/mdm/update?terminalId=t_46j1I7ul38rKdyub&directLaunchEnabled=true&statusBarEnabled=true&navigationBarEnabled=true&appSingleAppModeEnabled=true
// http://example.com/buckaroo/terminals/smart/mdm?terminalId=t_46j1I7ul38rKdyub
// http://example.com/buckaroo/terminals/smart/reboot?terminalId=t_46j1I7ul38rKdyub
// http://example.com/buckaroo/terminals/smart/status?terminalId=t_46j1I7ul38rKdyub
// http://example.com/buckaroo/terminals/internal?terminalId=t_46JOG2aP3rpaOnnt
// http://example.com/buckaroo/terminals/internal?terminalId=t_46JOG2aT0aZr64dK
// http://example.com/buckaroo/terminals/internal/update?terminalId=t_46JOG2aP3rpaOnnt&name=Updated%20Internal&receiptName=Example%20Store&receiptLocation=Utrecht&qrReceiptEnabled=true&customerReceiptMode=Enabled&merchantReceiptMode=Enabled&terminalLoginOptions=NoLogin&fixedAmountMode=MandatoryNoDeviation&wecrEnabled=true&wecrSupplier=None
// http://example.com/buckaroo/terminals/internal/status?terminalId=t_46JOG2aP3rpaOnnt
// http://example.com/buckaroo/terminals/internal/cancel?terminalId=t_46JOG2aP3rpaOnnt
// http://example.com/buckaroo/reporting/reports/create?reportDefinitionId=rd_44JsaGIUk2lqi7S2&scope=Merchant&email=test@example.com&recipientName=John%20Doe&filters=%5B%7B%22name%22%3A%22CardGrouping%22%2C%22value%22%3A%22ByTerminalAndBrand%22%7D%5D
// http://example.com/buckaroo/reporting/reports?id=rpt_1VvGC8YC6B3H3ezv
// http://example.com/buckaroo/reporting/reports/search
// http://example.com/buckaroo/reporting/reports/search?type=Manual
// http://example.com/buckaroo/reporting/reports/search?status=Done
// http://example.com/buckaroo/reporting/reports/search?user=John%20Doe
// http://example.com/buckaroo/reporting/reports/search?dateFrom=2024-12-01T00:00:00%2B01:00&dateTo=2024-12-13T23:59:59%2B01:00
// http://example.com/buckaroo/reporting/definitions
// http://example.com/buckaroo/reporting/definitions/search
// http://example.com/buckaroo/reporting/schedules/create?reportDefinitionId=rd_44JsaGIoUV4g81MN&scope=Merchant&interval=Daily&email=test@example.com
// http://example.com/buckaroo/reporting/schedules/create?reportDefinitionId=rd_44JsaGIoUV4g81MN&scope=MerchantGroup&interval=Daily&email=test@example.com&filters=%5B%7B%22name%22%3A%22CardGrouping%22%2C%22value%22%3A%22ByTerminalAndBrand%22%7D%5D
// http://example.com/buckaroo/reporting/schedules/update?id=rs_44OMSDsFCElwWNHs&reportDefinitionId=rd_44JsaGIoUV4g81MN&scope=Merchant&interval=Daily&status=Active&email=test@example.com&filters=%5B%7B%22name%22%3A%22CardGrouping%22%2C%22value%22%3A%22ByTerminalAndBrand%22%7D%5D
// http://example.com/buckaroo/reporting/schedules?id=rs_44OMSDsFCElwWNHs
// http://example.com/buckaroo/reporting/schedules/delete?id=rs_44OMSDsFCElwWNHs
// http://example.com/buckaroo/reporting/schedules/search
// http://example.com/buckaroo/reporting/schedules/search?limit=50
// http://example.com/buckaroo/sales/create?reference=test-002&amount=10.00&paymentMethod=IDeal&email=test@example.com&terminalId=12345&currency=EUR
// http://example.com/buckaroo/sales/search
// http://example.com/buckaroo/sales/search?storeId=s_1234abcd
// http://example.com/buckaroo/sales/search?terminalId=t_46JOG2aP3rpaOnnt
// http://example.com/buckaroo/sales/search?status=Open&sequenceType=OneOff
// http://example.com/buckaroo/sales/search?fromDateTime=2026-05-01T00:00:00%2B02:00&toDateTime=2026-05-22T23:59:59%2B02:00
// http://example.com/buckaroo/search_transactions
// http://example.com/buckaroo/search_transactions?storeId=s_1234abcd
// http://example.com/buckaroo/search_transactions?terminalId=t_46JOG2aP3rpaOnnt
// http://example.com/buckaroo/search_transactions?storeId=s_1234abcd&terminalId=t_46JOG2aP3rpaOnnt
// http://example.com/buckaroo/search_transactions?status=Successful&paymentMethodCode=pin&merchantReference=ABC123
// http://example.com/buckaroo/transactions?id=t_abcd1234efgh5678
// http://example.com/buckaroo/sales?saleId=sl_47EzUGuSMyC09ihl
// http://example.com/buckaroo/sales/cancel?saleId=sl_abcd1234efgh5678
// http://example.com/buckaroo/sales/reference?reference=unique-reference
// http://example.com/buckaroo/search?q=test
// http://example.com/buckaroo/search?needle=test&resourceType=Sale
// http://example.com/buckaroo/search?needle=t_abcd&resourceType=Terminal&limit=25
// http://example.com/buckaroo/services/subscriptions/search
// http://example.com/buckaroo/services/subscriptions/search?serviceCode=Shield
// http://example.com/buckaroo/services/subscriptions/search?status=Enabled
// http://example.com/buckaroo/services/subscriptions?id=svs_1nASC78SMNYZ6Oqi
// http://example.com/buckaroo/services/subscriptions/update?id=svs_1nASC78SMNYZ6Oqi&action=Activate
// http://example.com/buckaroo/services/subscriptions/update?id=svs_1nASC78SMNYZ6Oqi&action=Deactivate
// http://example.com/buckaroo/services/subscriptions/reprioritise?code=Shield&orderedSubscriptionIds=svs_first,svs_second,svs_third
// http://example.com/buckaroo/stores?status=Active
// http://example.com/buckaroo/stores/create?type=Online&name=Store%20A&status=Test&url=https://teststore.nl&city=Utrecht&country=NL
// http://example.com/buckaroo/stores/search
// http://example.com/buckaroo/stores/search?status=Active
// http://example.com/buckaroo/stores/search?type=Online
// http://example.com/buckaroo/stores/search?status=Active&type=Online&mcc=7523
// http://example.com/buckaroo/stores/update?storeId=s_46R1G8q4&name=Updated%20Store&status=Active&type=Online
// http://example.com/buckaroo/stores/update?storeId=s_46R1G8q4&name=Nimrodstraat,%20TILBURG&status=Active&type=Physical
// http://example.com/buckaroo/webhooks/events
// http://example.com/buckaroo/webhooks/search
// http://example.com/buckaroo/webhooks/search?storeId=s_1234abcd
// http://example.com/buckaroo/webhooks/search?eventType=TRANSACTION.CREATE
// http://example.com/buckaroo/webhooks/search?storeIds=s_1234abcd,s_abcd1234&eventTypes=TRANSACTION.CREATE,TRANSACTION.REFUND
// http://example.com/buckaroo/webhooks/create?url=https%3A%2F%2Fapi.example.com%2Fwebhook&secret=testsecret&eventTypes=SALE.CREATE,SALE.UPDATE&maxConcurrency=10
// http://example.com/buckaroo/webhooks/create?url=https%3A%2F%2Fapi.example.com%2Fwebhook&secret=testsecret&storeId=s_1234abcd&eventType=TRANSACTION.CREATE
// http://example.com/buckaroo/webhooks?id=es_47NSHCAQALLG2IXs
// http://example.com/buckaroo/webhooks/update?id=es_47NSHCAQALLG2IXs&url=https%3A%2F%2Fapi.example.com%2Fwebhook&maxConcurrency=10&eventTypes=SALE.CREATE,SALE.UPDATE&status=Active
// http://example.com/buckaroo/webhooks/delete?id=es_47NSHCAQALLG2IXs

Example routes

    $router->mount("/buckaroo", function () use ($router) {
        $router->get("/accounts", "BuckarooController@getAccounts");
        $router->get("/accounts/account", "BuckarooController@getAccount");
        $router->get("/accounts/update-payout-settings", "BuckarooController@updateAccountPayoutSettings");
        $router->get("/create_api_key", "BuckarooController@createApiKey");
        $router->get("/apikeys", "BuckarooController@getApiKeys");
        $router->get("/apikey", "BuckarooController@getApiKey");
        $router->get("/apikey/delete", "BuckarooController@deleteApiKey");
        $router->get("/apikey/update", "BuckarooController@updateApiKey");
        $router->get("/apikey/decrypt", "BuckarooController@decryptApiKey");
        $router->get("/applications/search", "BuckarooController@searchApplications");
        $router->get("/applications", "BuckarooController@getApplication");
        $router->get("/applications/create", "BuckarooController@createApplication");
        $router->get("/applications/installations/search", "BuckarooController@searchApplicationInstallations");
        $router->get("/applications/installations", "BuckarooController@getApplicationInstallation");
        $router->get("/create_or_update_customer", "BuckarooController@createOrUpdateCustomer");
        $router->get("/search_customers", "BuckarooController@searchCustomers");
        $router->get("/customer", "BuckarooController@getCustomer");
        $router->get("/customer/delete", "BuckarooController@deleteCustomer");
        $router->get("/installations/search", "BuckarooController@searchInstallations");
        $router->get("/installations", "BuckarooController@getInstallation");
        $router->get("/installations/update", "BuckarooController@updateInstallation");
        $router->get("/invoices", "BuckarooController@getInvoice");
        $router->get("/invoices/attachment", "BuckarooController@getInvoiceAttachment");
        $router->get("/invoices/creditnote", "BuckarooController@getInvoiceCreditNote");
        $router->get("/invoices/search", "BuckarooController@searchInvoices");
        $router->get("/merchant", "BuckarooController@getMerchant");
        $router->get("/merchant/update", "BuckarooController@updateMerchant");
        $router->get("/merchant/features", "BuckarooController@getMerchantFeatures");
        $router->get("/merchant/legalentity", "BuckarooController@getMerchantLegalEntity");
        $router->get("/get_access_token", "BuckarooController@getAccessToken");
        $router->get("/oauth/convert", "BuckarooController@convertZitadelToken");
        $router->get("/paymentmethods/subscriptions/search", "BuckarooController@searchPaymentMethodSubscriptions");
        $router->get("/paymentmethods/subscriptions", "BuckarooController@getPaymentMethodSubscription");
        $router->get("/paymentmethods/subscriptions/update", "BuckarooController@updatePaymentMethodSubscription");
        $router->get("/paymentmethods/subscriptions/reprioritise", "BuckarooController@reprioritisePaymentMethodSubscriptions");
        $router->get("/payouts/search", "BuckarooController@searchPayouts");
        $router->get("/payouts", "BuckarooController@getPayout");
        $router->get("/terminals/search", "BuckarooController@searchTerminals");
        $router->get("/terminals/smart", "BuckarooController@getSmartTerminal");
        $router->get("/terminals/smart/update", "BuckarooController@updateSmartTerminal");
        $router->get("/terminals/smart/cancel", "BuckarooController@cancelSmartTerminalAction");
        $router->get("/terminals/smart/mdm/update", "BuckarooController@updateSmartTerminalMdmSettings");
        $router->get("/terminals/smart/mdm", "BuckarooController@getSmartTerminalMdmSettings");
        $router->get("/terminals/smart/reboot", "BuckarooController@rebootSmartTerminal");
        $router->get("/terminals/smart/status", "BuckarooController@getSmartTerminalConnectionStatus");
        $router->get("/terminals/internal", "BuckarooController@getInternalTerminal");
        $router->get("/terminals/internal/update", "BuckarooController@updateInternalTerminal");
        $router->get("/terminals/internal/status", "BuckarooController@getInternalTerminalConnectionStatus");
        $router->get("/terminals/internal/cancel", "BuckarooController@cancelInternalTerminalAction");
        $router->get("/reporting/reports/create", "BuckarooController@createReport");
        $router->get("/reporting/reports", "BuckarooController@getReport");
        $router->get("/reporting/reports/search", "BuckarooController@searchReports");
        $router->get("/reporting/definitions", "BuckarooController@getReportDefinitions");
        $router->get("/reporting/definitions/search", "BuckarooController@searchReportDefinitions");
        $router->get("/reporting/schedules/create", "BuckarooController@createReportSchedule");
        $router->get("/reporting/schedules/update", "BuckarooController@updateReportSchedule");
        $router->get("/reporting/schedules", "BuckarooController@getReportSchedule");
        $router->get("/reporting/schedules/delete", "BuckarooController@deleteReportSchedule");
        $router->get("/reporting/schedules/search", "BuckarooController@searchReportSchedules");
        $router->get("/sales/create", "BuckarooController@createSale");
        $router->get("/sales/search", "BuckarooController@searchSales");
        $router->get("/search_transactions", "BuckarooController@searchTransactions");
        $router->get("/transactions", "BuckarooController@getTransaction");
        $router->get("/sales", "BuckarooController@getSale");
        $router->get("/sales/cancel", "BuckarooController@cancelSale");
        $router->get("/sales/reference", "BuckarooController@getSaleByReference");
        $router->get("/search", "BuckarooController@globalSearch");
        $router->get("/services/subscriptions/search", "BuckarooController@searchServiceSubscriptions");
        $router->get("/services/subscriptions", "BuckarooController@getServiceSubscription");
        $router->get("/services/subscriptions/update", "BuckarooController@updateServiceSubscription");
        $router->get("/services/subscriptions/reprioritise", "BuckarooController@reprioritiseServiceSubscriptions");
        $router->get("/stores", "BuckarooController@getStores");
        $router->get("/stores/create", "BuckarooController@createStore");
        $router->get("/stores/search", "BuckarooController@searchStores");
        $router->get("/stores/update", "BuckarooController@updateStore");
        $router->get("/webhooks/events", "BuckarooController@getWebhookEventTypes");
        $router->get("/webhooks/search", "BuckarooController@searchWebhooks");
        $router->get("/webhooks/create", "BuckarooController@createWebhook");
        $router->get("/webhooks", "BuckarooController@getWebhook");
        $router->get("/webhooks/update", "BuckarooController@updateWebhook");
        $router->get("/webhooks/delete", "BuckarooController@deleteWebhook");
    });

Example code

<?php

use Dotenv\Dotenv;
use PinVandaag\BuckarooAPI\BuckarooAPIClient;

final class BuckarooController
{
    private const API_KEY_NAME = 'pinvandaag-integration-key';
    private const SEARCH_LIMIT = 100;

    private BuckarooAPIClient $apiClient;
    private \Nette\Database\Explorer $database_dashboard;

    public function __construct()
    {
        $dotenv = Dotenv::createImmutable(__DIR__ . "/../../../../");
        $dotenv->safeLoad();
        $dotenv->required(['BUCKAROO_CLIENT_ID', 'BUCKAROO_CLIENT_SECRET'])->notEmpty();

        $this->apiClient = (new BuckarooAPIClient())
            ->configure(
                clientId: $_ENV['BUCKAROO_CLIENT_ID'],
                clientSecret:  $_ENV['BUCKAROO_CLIENT_SECRET'],
                baseUri: 'https://api.buckaroo.io'
            );

        $this->database_dashboard = (new \database\DataSource)->main();
    }

    private function getStoredApiKey(): string
    {
        $apiKeyRecord = $this->database_dashboard->query('
            SELECT *
            FROM buckaroo_api
            LIMIT 1
        ')->fetch();

        if ($apiKeyRecord) {
            return $apiKeyRecord->api_key;
        }

        return $this->createApiKey()->key;
    }

    public function getAccounts()
    {
        $accessToken = $this->getAccessToken();

        $result = $this->apiClient->getAccounts(
            accessToken: $accessToken->accessToken,
        );

        $this->jsonResponse([
            'count' => count($result->accounts),
            'accounts' => $result->accounts,
        ]);
    }

    public function getAccount()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing account id.');
        }

        $accessToken = $this->getAccessToken();

        $account = $this->apiClient->getAccount(
            accessToken: $accessToken->accessToken,
            id: $id,
        );

        $this->jsonResponse([
            'account' => $account,
        ]);
    }

    public function updateAccountPayoutSettings()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing account id.');
        }

        $accessToken = $this->getAccessToken();

        $settings = $this->apiClient->updateAccountPayoutSettings(
            accessToken: $accessToken->accessToken,
            id: $id,
            payload: [
                'payoutIban' => $_GET['payoutIban'] ?? null,
                'grouping' => $_GET['grouping'] ?? null,
                'payoutDays' => isset($_GET['payoutDays'])
                    ? array_map('intval', array_values(array_filter(array_map('trim', explode(',', $_GET['payoutDays'])))))
                    : null,
                'payoutInterval' => $_GET['payoutInterval'] ?? null,
            ],
        );

        $this->jsonResponse([
            'settings' => $settings,
        ]);
    }

    public function createApiKey()
    {
        $token = $this->getAccessToken();

        $apiKey = $this->apiClient->createApiKey(
            accessToken: $token->accessToken,
            name: self::API_KEY_NAME,
        );

        $this->database_dashboard->table('buckaroo_api')->insert([
            'api_key' => $apiKey->key,
        ]);

        return $apiKey;
    }

    private function getAllApiKeys(
        string $accessToken,
    ): array {
        $allApiKeys = [];
        $continuationToken = null;

        do {
            $result = $this->apiClient->getApiKeys(
                accessToken: $accessToken,
                continuationToken: $continuationToken,
            );

            $allApiKeys = array_merge($allApiKeys, $result->apiKeys);
            $continuationToken = $result->nextContinuationToken;
        } while ($continuationToken !== null && $continuationToken !== '');

        return $allApiKeys;
    }

    public function getApiKeys()
    {
        $accessToken = $this->getAccessToken();

        $apiKeys = $this->getAllApiKeys(
            accessToken: $accessToken->accessToken,
        );

        $this->jsonResponse([
            'count' => count($apiKeys),
            'apiKeys' => $apiKeys,
        ]);
    }

    public function getApiKey()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing API key id.');
        }

        $accessToken = $this->getAccessToken();

        $apiKey = $this->apiClient->getApiKey(
            accessToken: $accessToken->accessToken,
            id: $id,
        );

        $this->jsonResponse([
            'apiKey' => $apiKey,
        ]);
    }

    public function deleteApiKey()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing API key id.');
        }

        $accessToken = $this->getAccessToken();

        $this->apiClient->deleteApiKey(
            accessToken: $accessToken->accessToken,
            id: $id,
        );

        $this->jsonResponse([
            'success' => true,
            'message' => sprintf('Buckaroo API key "%s" deleted.', $id),
        ]);
    }

    public function updateApiKey()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing API key id.');
        }

        $scopes = $_GET['scopes'] ?? null;

        if ($scopes === null || $scopes === '') {
            throw new \RuntimeException('Missing scopes.');
        }

        $accessToken = $this->getAccessToken();

        $apiKey = $this->apiClient->updateApiKey(
            accessToken: $accessToken->accessToken,
            id: $id,
            scopes: $scopes,
        );

        $this->jsonResponse([
            'apiKey' => $apiKey,
        ]);
    }

    public function decryptApiKey()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing API key id.');
        }

        $accessToken = $this->getAccessToken();

        $apiKey = $this->apiClient->decryptApiKey(
            accessToken: $accessToken->accessToken,
            id: $id,
        );

        $this->jsonResponse([
            'apiKey' => $apiKey,
        ]);
    }

    private function searchAllApplications(
        string $accessToken,
        array $filters = [],
    ): array {
        $allApplications = [];
        $continuationToken = null;

        do {
            $filtersWithContinuation = $filters;

            if ($continuationToken !== null && $continuationToken !== '') {
                $filtersWithContinuation['continuationToken'] = $continuationToken;
            }

            $result = $this->apiClient->searchApplications(
                accessToken: $accessToken,
                filters: $filtersWithContinuation,
            );

            $allApplications = array_merge($allApplications, $result->applications);
            $continuationToken = $result->nextContinuationToken;
        } while ($continuationToken !== null && $continuationToken !== '');

        return $allApplications;
    }

    public function searchApplications()
    {
        $accessToken = $this->getAccessToken();

        $applications = $this->searchAllApplications(
            accessToken: $accessToken->accessToken,
            filters: [
                'name' => $_GET['name'] ?? null,
            ],
        );

        $this->jsonResponse([
            'count' => count($applications),
            'applications' => $applications,
        ]);
    }

    public function getApplication()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing application id.');
        }

        $accessToken = $this->getAccessToken();

        $application = $this->apiClient->getApplication(
            accessToken: $accessToken->accessToken,
            id: $id,
        );

        $this->jsonResponse([
            'application' => $application,
        ]);
    }

    public function createApplication()
    {
        $accessToken = $this->getAccessToken();

        $eventTypes = null;
        if (isset($_GET['eventTypes'])) {
            $eventTypes = array_values(array_filter(array_map('trim', explode(',', $_GET['eventTypes']))));
        }

        $eventSubscription = null;
        if (isset($_GET['webhookUrl']) || isset($_GET['webhookSecret']) || $eventTypes !== null) {
            $eventSubscription = [
                'url' => $_GET['webhookUrl'] ?? null,
                'secret' => $_GET['webhookSecret'] ?? null,
                'eventTypes' => $eventTypes ?? [],
            ];
        }

        $application = $this->apiClient->createApplication(
            accessToken: $accessToken->accessToken,
            application: [
                'name' => $_GET['name'] ?? 'PinVandaag Test Application',
                'applicationType' => $_GET['applicationType'] ?? 'Merchant',
                'scopes' => $_GET['scopes'] ?? 'sale:read',
                'eventSubscription' => $eventSubscription,
            ],
        );

        $this->jsonResponse([
            'application' => $application,
            'warning' => 'Store clientSecret securely. Buckaroo only returns it once.',
        ]);
    }

    private function searchAllApplicationInstallations(
        string $accessToken,
        string $id,
        array $filters = [],
    ): array {
        $allInstallations = [];
        $continuationToken = null;

       do {
            $filtersWithContinuation = $filters;

            if ($continuationToken !== null && $continuationToken !== '') {
                $filtersWithContinuation['continuationToken'] = $continuationToken;
            }

            $result = $this->apiClient->searchApplicationInstallations(
                accessToken: $accessToken,
                id: $id,
                filters: $filtersWithContinuation,
            );

            $allInstallations = array_merge($allInstallations, $result->installations);
            $continuationToken = $result->nextContinuationToken;
        } while ($continuationToken !== null && $continuationToken !== '');

        return $allInstallations;
    }

    public function searchApplicationInstallations()
    {
        $id = $_GET['id'] ?? $_GET['applicationId'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing application id.');
        }

        $accessToken = $this->getAccessToken();

        $installations = $this->searchAllApplicationInstallations(
            accessToken: $accessToken->accessToken,
            id: $id,
            filters: [
                'merchantId' => $_GET['merchantId'] ?? null,
                'statuses' => isset($_GET['status']) ? [$_GET['status']] : null,
            ],
        );

        $this->jsonResponse([
            'count' => count($installations),
            'installations' => $installations,
        ]);
    }

    public function getApplicationInstallation()
    {
        $id = $_GET['id'] ?? $_GET['applicationId'] ?? null;
        $installationId = $_GET['installationId'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing application id.');
        }

        if ($installationId === null || $installationId === '') {
            throw new \RuntimeException('Missing installation id.');
        }

        $accessToken = $this->getAccessToken();

        $installation = $this->apiClient->getApplicationInstallation(
            accessToken: $accessToken->accessToken,
            id: $id,
            installationId: $installationId,
        );

        $this->jsonResponse([
            'installation' => $installation,
        ]);
    }

    public function createOrUpdateCustomer()
    {
        $accessToken = $this->getAccessToken();

        $reference = $_GET['customerReference'] ?? $_GET['reference'] ?? null;

        if ($reference === null || $reference === '') {
            throw new \RuntimeException('Missing customerReference.');
        }

        $customer = $this->apiClient->createOrUpdateCustomer(
            accessToken: $accessToken->accessToken,
            customer: [
                'reference' => $reference,
                'address' => [
                    'street' => $_GET['street'] ?? null,
                    'houseNumber' => $_GET['houseNumber'] ?? null,
                    'postalCode' => $_GET['postalCode'] ?? null,
                    'city' => $_GET['city'] ?? null,
                    'country' => $_GET['country'] ?? 'NL',
                ],
                'title' => $_GET['title'] ?? null,
                'givenName' => $_GET['givenName'] ?? null,
                'familyName' => $_GET['familyName'] ?? null,
                'email' => $_GET['email'] ?? null,
                'phoneNumber' => $_GET['phoneNumber'] ?? null,
                'dateOfBirth' => $_GET['dateOfBirth'] ?? null,
                'organization' => [
                    'name' => $_GET['organizationName'] ?? null,
                    'vatNumber' => $_GET['vatNumber'] ?? null,
                ],
                'preferredLocale' => $_GET['preferredLocale'] ?? 'nl-NL',
            ],
        );

        $this->jsonResponse([
            'customer' => $customer,
        ]);
    }

    private function searchAllCustomers(
        string $accessToken,
        ?string $email = null,
        ?string $customerReference = null,
    ): array {
        $allCustomers = [];
        $continuationToken = null;

        do {
            $filters = [
                'email' => $email,
                'customerReference' => $customerReference,
            ];

            if ($continuationToken !== null && $continuationToken !== '') {
                $filters['continuationToken'] = $continuationToken;
            }

            $result = $this->apiClient->searchCustomers(
                accessToken: $accessToken,
                filters: $filters,
            );

            $allCustomers = array_merge($allCustomers, $result->customers);
            $continuationToken = $result->nextContinuationToken;
        } while ($continuationToken !== null && $continuationToken !== '');

        return $allCustomers;
    }

    public function searchCustomers()
    {
        $accessToken = $this->getAccessToken();

        $customers = $this->searchAllCustomers(
            accessToken: $accessToken->accessToken,
            email: $_GET['email'] ?? null,
            customerReference: $_GET['customerReference'] ?? null,
        );

        $this->jsonResponse([
            'count' => count($customers),
            'customers' => $customers,
        ]);
    }

    public function getCustomer()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing customer id.');
        }

        $accessToken = $this->getAccessToken();

        $customer = $this->apiClient->getCustomer(
            accessToken: $accessToken->accessToken,
            id: $id,
        );

        $this->jsonResponse([
            'customer' => $customer,
        ]);
    }

    public function deleteCustomer()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing customer id.');
        }

        $accessToken = $this->getAccessToken();

        $this->apiClient->deleteCustomer(
            accessToken: $accessToken->accessToken,
            id: $id,
        );

        $this->jsonResponse([
            'deleted' => true,
            'id' => $id,
        ]);
    }

    private function searchAllInstallations(
        string $accessToken,
        array $filters = [],
    ): array {
        $allInstallations = [];
        $continuationToken = null;

        do {
            $filtersWithContinuation = $filters;

            if ($continuationToken !== null && $continuationToken !== '') {
                $filtersWithContinuation['continuationToken'] = $continuationToken;
            }

            $result = $this->apiClient->searchInstallations(
                accessToken: $accessToken,
                filters: $filtersWithContinuation,
            );

            $allInstallations = array_merge($allInstallations, $result->installations);
            $continuationToken = $result->nextContinuationToken;
        } while ($continuationToken !== null && $continuationToken !== '');

        return $allInstallations;
    }

    public function searchInstallations()
    {
        $accessToken = $this->getAccessToken();

        $installations = $this->searchAllInstallations(
            accessToken: $accessToken->accessToken,
            filters: [
                'statuses' => isset($_GET['status']) ? [$_GET['status']] : null,
            ],
        );

        $this->jsonResponse([
            'count' => count($installations),
            'installations' => $installations,
        ]);
    }

    public function getInstallation()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing installation id.');
        }

        $accessToken = $this->getAccessToken();

        $installation = $this->apiClient->getInstallation(
            accessToken: $accessToken->accessToken,
            id: $id,
        );

        $this->jsonResponse([
            'installation' => $installation,
        ]);
    }

    public function updateInstallation()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing installation id.');
        }

        $status = $_GET['status'] ?? null;

        if ($status === null || $status === '') {
            throw new \RuntimeException('Missing installation status.');
        }

        $eventCodes = null;
        if (isset($_GET['eventSubscriptionEventCodes'])) {
            $eventCodes = array_values(array_filter(array_map('trim', explode(',', $_GET['eventSubscriptionEventCodes']))));
        } elseif (isset($_GET['eventCodes'])) {
            $eventCodes = array_values(array_filter(array_map('trim', explode(',', $_GET['eventCodes']))));
        }

        $accessToken = $this->getAccessToken();

        $installation = $this->apiClient->updateInstallation(
            accessToken: $accessToken->accessToken,
            id: $id,
            payload: [
                'status' => $status,
                'scopes' => $_GET['scopes'] ?? null,
                'eventSubscriptionEventCodes' => $eventCodes,
            ],
        );

        $this->jsonResponse([
            'installation' => $installation,
        ]);
    }

    public function getInvoice()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing invoice id.');
        }

        $accessToken = $this->getAccessToken();

        $invoice = $this->apiClient->getInvoice(
            accessToken: $accessToken->accessToken,
            id: $id,
        );

        $this->jsonResponse([
            'invoice' => $invoice,
        ]);
    }

    public function getInvoiceAttachment()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing invoice attachment id.');
        }

        $accessToken = $this->getAccessToken();

        $attachment = $this->apiClient->getInvoiceAttachment(
            accessToken: $accessToken->accessToken,
            id: $id,
        );

        $this->jsonResponse([
            'attachment' => $attachment,
        ]);
    }

    public function getInvoiceCreditNote()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing invoice credit note id.');
        }

        $accessToken = $this->getAccessToken();

        $creditNote = $this->apiClient->getInvoiceCreditNote(
            accessToken: $accessToken->accessToken,
            id: $id,
        );

        $this->jsonResponse([
            'creditNote' => $creditNote,
        ]);
    }

    private function searchAllInvoices(
        string $accessToken,
        array $filters = [],
    ): array {
        $allInvoices = [];
        $continuationToken = null;

        do {
            $filtersWithContinuation = $filters;

            if ($continuationToken !== null && $continuationToken !== '') {
                $filtersWithContinuation['continuationToken'] = $continuationToken;
            }

            $result = $this->apiClient->searchInvoices(
                accessToken: $accessToken,
                filters: $filtersWithContinuation,
            );

            $allInvoices = array_merge($allInvoices, $result->invoices);
            $continuationToken = $result->nextContinuationToken;
        } while ($continuationToken !== null && $continuationToken !== '');

        return $allInvoices;
    }

    public function searchInvoices()
    {
        $accessToken = $this->getAccessToken();

        $filters = [
            'currency' => $_GET['currency'] ?? null,
            'invoiceNumber' => $_GET['invoiceNumber'] ?? null,
            'paymentStatuses' => isset($_GET['paymentStatus']) ? [$_GET['paymentStatus']] : null,
            'fromAmount' => $_GET['fromAmount'] ?? null,
            'toAmount' => $_GET['toAmount'] ?? null,
            'fromInvoiceDate' => $_GET['fromInvoiceDate'] ?? null,
            'toInvoiceDate' => $_GET['toInvoiceDate'] ?? null,
            'limit' => self::SEARCH_LIMIT,
        ];

        $invoices = $this->searchAllInvoices(
            accessToken: $accessToken->accessToken,
            filters: $filters,
        );

        $this->jsonResponse([
            'count' => count($invoices),
            'invoices' => $invoices,
        ]);
    }

    public function getMerchant()
    {
        $accessToken = $this->getAccessToken();

        $merchant = $this->apiClient->getMerchant(
            accessToken: $accessToken->accessToken,
        );

        $this->jsonResponse([
            'merchant' => $merchant,
        ]);
    }

    public function updateMerchant()
    {
        $defaultLanguage = $_GET['defaultLanguage'] ?? null;

        if ($defaultLanguage === null || $defaultLanguage === '') {
            throw new \RuntimeException('Missing defaultLanguage.');
        }

        $accessToken = $this->getAccessToken();

        $merchant = $this->apiClient->updateMerchant(
            accessToken: $accessToken->accessToken,
            defaultLanguage: $defaultLanguage,
        );

        $this->jsonResponse([
            'merchant' => $merchant,
        ]);
    }

    public function getMerchantFeatures()
    {
        $accessToken = $this->getAccessToken();

        $features = $this->apiClient->getMerchantFeatures(
            accessToken: $accessToken->accessToken,
            continuationToken: $_GET['continuationToken'] ?? null,
        );

        $this->jsonResponse([
            'features' => $features,
            'nextContinuationToken' => $features->nextContinuationToken,
        ]);
    }

    public function getMerchantLegalEntity()
    {
        $accessToken = $this->getAccessToken();

        $legalEntity = $this->apiClient->getMerchantLegalEntity(
            accessToken: $accessToken->accessToken,
        );

        $this->jsonResponse([
            'legalEntity' => $legalEntity,
        ]);
    }

    public function getAccessToken()
    {
        $accessToken = $this->apiClient->getAccessToken();
        // print_r($accessToken->accessToken);
        return $accessToken;
    }

    public function convertZitadelToken()
    {
        $merchantId = $_GET['merchantId'] ?? $_GET['merchant_id'] ?? null;

        if ($merchantId === null || $merchantId === '') {
            throw new \RuntimeException('Missing merchantId.');
        }

        $zitadelToken = $_GET['zitadelToken'] ?? $_GET['zitadel_token'] ?? null;

        if ($zitadelToken === null || $zitadelToken === '') {
            throw new \RuntimeException('Missing zitadelToken.');
        }

        $accessToken = $this->apiClient->convertZitadelToken(
            merchantId: $merchantId,
            zitadelToken: $zitadelToken,
        );

        $this->jsonResponse([
            'accessToken' => $accessToken,
        ]);
    }

    public function searchPaymentMethodSubscriptions()
    {
        $accessToken = $this->getAccessToken();

        $filters = [
            'paymentMethodCode' => $_GET['paymentMethodCode'] ?? null,
            'currencies' => isset($_GET['currency']) ? [$_GET['currency']] : null,
            'statuses' => isset($_GET['status']) ? [$_GET['status']] : null,
        ];

        $result = $this->apiClient->searchPaymentMethodSubscriptions(
            accessToken: $accessToken->accessToken,
            filters: $filters,
        );

        $this->jsonResponse([
            'count' => count($result->subscriptions),
            'subscriptions' => $result->subscriptions,
        ]);
    }

    public function getPaymentMethodSubscription()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing payment method subscription id.');
        }

        $accessToken = $this->getAccessToken();

        $subscription = $this->apiClient->getPaymentMethodSubscription(
            accessToken: $accessToken->accessToken,
            id: $id,
        );

        $this->jsonResponse([
            'subscription' => $subscription,
        ]);
    }

    public function updatePaymentMethodSubscription()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing payment method subscription id.');
        }

        $action = $_GET['action'] ?? null;

        if ($action === null || $action === '') {
            throw new \RuntimeException('Missing payment method subscription action.');
        }

        $accessToken = $this->getAccessToken();

        $subscription = $this->apiClient->updatePaymentMethodSubscription(
            accessToken: $accessToken->accessToken,
            id: $id,
            payload: [
                'action' => $action,
                'currencies' => isset($_GET['currencies'])
                    ? array_values(array_filter(array_map('trim', explode(',', $_GET['currencies']))))
                    : (isset($_GET['currency']) ? [$_GET['currency']] : null),
            ],
        );

        $this->jsonResponse([
            'subscription' => $subscription,
        ]);
    }

    public function reprioritisePaymentMethodSubscriptions()
    {
        $code = $_GET['code'] ?? $_GET['paymentMethodCode'] ?? null;

        if ($code === null || $code === '') {
            throw new \RuntimeException('Missing payment method code.');
        }

        $orderedSubscriptionIds = [];

        if (isset($_GET['orderedSubscriptionIds'])) {
            $orderedSubscriptionIds = array_values(array_filter(array_map('trim', explode(',', $_GET['orderedSubscriptionIds']))));
        } elseif (isset($_GET['ids'])) {
            $orderedSubscriptionIds = array_values(array_filter(array_map('trim', explode(',', $_GET['ids']))));
        }

        if ($orderedSubscriptionIds === []) {
            throw new \RuntimeException('Missing orderedSubscriptionIds.');
        }

        $accessToken = $this->getAccessToken();

        $result = $this->apiClient->reprioritisePaymentMethodSubscriptions(
            accessToken: $accessToken->accessToken,
            code: $code,
            orderedSubscriptionIds: $orderedSubscriptionIds,
        );

        $this->jsonResponse([
            'count' => count($result->subscriptions),
            'subscriptions' => $result->subscriptions,
        ]);
    }

    private function searchAllPayouts(
        string $accessToken,
        array $filters = [],
    ): array {
        $allPayouts = [];
        $continuationToken = null;

        do {
            $filtersWithContinuation = $filters;

            if ($continuationToken !== null && $continuationToken !== '') {
                $filtersWithContinuation['continuationToken'] = $continuationToken;
            }

            $result = $this->apiClient->searchPayouts(
                accessToken: $accessToken,
                filters: $filtersWithContinuation,
            );

            $allPayouts = array_merge($allPayouts, $result->payouts);
            $continuationToken = $result->nextContinuationToken;
        } while ($continuationToken !== null && $continuationToken !== '');

        return $allPayouts;
    }

    public function searchPayouts()
    {
        $accessToken = $this->getAccessToken();

        $filters = [
            'fromDate' => $_GET['fromDate'] ?? null,
            'toDate' => $_GET['toDate'] ?? null,
            'status' => $_GET['status'] ?? null,
            'currency' => $_GET['currency'] ?? null,
            'limit' => self::SEARCH_LIMIT,
        ];

        $payouts = $this->searchAllPayouts(
            accessToken: $accessToken->accessToken,
            filters: $filters,
        );

        $this->jsonResponse([
            'count' => count($payouts),
            'payouts' => $payouts,
        ]);
    }

    public function getPayout()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing payout id.');
        }

        $accessToken = $this->getAccessToken();

        $payout = $this->apiClient->getPayout(
            accessToken: $accessToken->accessToken,
            id: $id,
        );

        $this->jsonResponse([
            'payout' => $payout,
        ]);
    }

    private function searchAllTerminals(
        string $accessToken,
        array $filters = [],
    ): array {
        $allTerminals = [];
        $continuationToken = null;

        do {
            $filtersWithContinuation = $filters;

            if ($continuationToken !== null && $continuationToken !== '') {
                $filtersWithContinuation['continuationToken'] = $continuationToken;
            }

            $result = $this->apiClient->searchTerminals(
                accessToken: $accessToken,
                filters: $filtersWithContinuation,
            );

            $allTerminals = array_merge($allTerminals, $result->terminals);
            $continuationToken = $result->nextContinuationToken;
        } while ($continuationToken !== null && $continuationToken !== '');

        return $allTerminals;
    }

    public function searchTerminals()
    {
        $accessToken = $this->getAccessToken();

        $filters = [
            'storeIds' => isset($_GET['storeId']) ? [$_GET['storeId']] : null,
            'statuses' => isset($_GET['status']) ? [$_GET['status']] : null,
            'limit' => self::SEARCH_LIMIT,
        ];

        $terminals = $this->searchAllTerminals(
            accessToken: $accessToken->accessToken,
            filters: $filters,
        );

        $this->jsonResponse([
            'count' => count($terminals),
            'terminals' => $terminals,
        ]);
    }

    public function getSmartTerminal()
    {
        $terminalId = $_GET['terminalId'] ?? $_GET['id'] ?? null;

        if ($terminalId === null || $terminalId === '') {
            throw new \RuntimeException('Missing terminalId.');
        }

        $accessToken = $this->getAccessToken();

        $terminal = $this->apiClient->getSmartTerminal(
            accessToken: $accessToken->accessToken,
            terminalId: $terminalId,
        );

        $this->jsonResponse([
            'terminal' => $terminal,
        ]);
    }

    public function updateSmartTerminal()
    {
        $terminalId = $_GET['terminalId'] ?? $_GET['id'] ?? null;

        if ($terminalId === null || $terminalId === '') {
            throw new \RuntimeException('Missing terminalId.');
        }

        $accessToken = $this->getAccessToken();

        $terminal = $this->apiClient->updateSmartTerminal(
            accessToken: $accessToken->accessToken,
            terminalId: $terminalId,
            terminal: [
                'name' => $_GET['name'] ?? null,
                'securitySettings' => [
                    'adminPin' => $_GET['adminPin'] ?? null,
                ],
                'paymentSettings' => [
                    'refundEnabled' => isset($_GET['refundEnabled'])
                        ? filter_var($_GET['refundEnabled'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
                        : null,
                    'refundPin' => $_GET['refundPin'] ?? null,
                    'forceConsumerPin' => isset($_GET['forceConsumerPin'])
                        ? filter_var($_GET['forceConsumerPin'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
                        : null,
                ],
                'receiptSettings' => [
                    'customerReceiptMode' => $_GET['customerReceiptMode'] ?? null,
                    'merchantReceiptMode' => $_GET['merchantReceiptMode'] ?? null,
                    'headerText' => $_GET['headerText'] ?? null,
                    'footerText' => $_GET['footerText'] ?? null,
                    'logo' => [
                        'encodedLogo' => $_GET['receiptLogo'] ?? null,
                    ],
                ],
                'customisationSettings' => [
                    'logo' => [
                        'encodedLogo' => $_GET['customLogo'] ?? null,
                    ],
                ],
                'terminalMode' => $_GET['terminalMode'] ?? null,
            ],
        );

        $this->jsonResponse([
            'terminal' => $terminal,
        ]);
    }

    public function cancelSmartTerminalAction()
    {
        $terminalId = $_GET['terminalId'] ?? $_GET['id'] ?? null;

        if ($terminalId === null || $terminalId === '') {
            throw new \RuntimeException('Missing terminalId.');
        }

        $accessToken = $this->getAccessToken();

        $this->apiClient->cancelSmartTerminalAction(
            accessToken: $accessToken->accessToken,
            terminalId: $terminalId,
        );

        $this->jsonResponse([
            'cancelled' => true,
            'terminalId' => $terminalId,
        ]);
    }

    public function updateSmartTerminalMdmSettings()
    {
        $terminalId = $_GET['terminalId'] ?? $_GET['id'] ?? null;

        if ($terminalId === null || $terminalId === '') {
            throw new \RuntimeException('Missing terminalId.');
        }

        $accessToken = $this->getAccessToken();

        $settings = $this->apiClient->updateSmartTerminalMdmSettings(
            accessToken: $accessToken->accessToken,
            terminalId: $terminalId,
            settings: [
                'appSettings' => [
                    'singleAppModeEnabled' => isset($_GET['appSingleAppModeEnabled'])
                        ? filter_var($_GET['appSingleAppModeEnabled'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
                        : null,
                ],
                'mdmSettings' => [
                    'directLaunchEnabled' => isset($_GET['directLaunchEnabled'])
                        ? filter_var($_GET['directLaunchEnabled'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
                        : null,
                    'statusBarEnabled' => isset($_GET['statusBarEnabled'])
                        ? filter_var($_GET['statusBarEnabled'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
                        : null,
                    'navigationBarEnabled' => isset($_GET['navigationBarEnabled'])
                        ? filter_var($_GET['navigationBarEnabled'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
                        : null,
                    'deviceOwnerGranted' => isset($_GET['deviceOwnerGranted'])
                        ? filter_var($_GET['deviceOwnerGranted'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
                        : null,
                    'singleAppModeEnabled' => isset($_GET['mdmSingleAppModeEnabled'])
                        ? filter_var($_GET['mdmSingleAppModeEnabled'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
                        : null,
                    'defaultLauncherEnabled' => isset($_GET['defaultLauncherEnabled'])
                        ? filter_var($_GET['defaultLauncherEnabled'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
                        : null,
                    'ntpServer' => $_GET['ntpServer'] ?? null,
                    'staticIpConfig' => [
                        'ipAddress' => $_GET['ipAddress'] ?? null,
                        'prefixLength' => isset($_GET['prefixLength']) ? (int) $_GET['prefixLength'] : null,
                        'gateway' => $_GET['gateway'] ?? null,
                        'dnsServers' => isset($_GET['dnsServers'])
                            ? array_values(array_filter(array_map('trim', explode(',', $_GET['dnsServers']))))
                            : null,
                    ],
                ],
            ],
        );

        $this->jsonResponse([
            'settings' => $settings,
        ]);
    }

    public function getSmartTerminalMdmSettings()
    {
        $terminalId = $_GET['terminalId'] ?? $_GET['id'] ?? null;

        if ($terminalId === null || $terminalId === '') {
            throw new \RuntimeException('Missing terminalId.');
        }

        $accessToken = $this->getAccessToken();

        $settings = $this->apiClient->getSmartTerminalMdmSettings(
            accessToken: $accessToken->accessToken,
            terminalId: $terminalId,
        );

        $this->jsonResponse([
            'settings' => $settings,
        ]);
    }

    public function rebootSmartTerminal()
    {
        $terminalId = $_GET['terminalId'] ?? $_GET['id'] ?? null;

        if ($terminalId === null || $terminalId === '') {
            throw new \RuntimeException('Missing terminalId.');
        }

        $accessToken = $this->getAccessToken();

        $this->apiClient->rebootSmartTerminal(
            accessToken: $accessToken->accessToken,
            terminalId: $terminalId,
        );

        $this->jsonResponse([
            'rebooted' => true,
            'terminalId' => $terminalId,
        ]);
    }

    public function getSmartTerminalConnectionStatus()
    {
        $terminalId = $_GET['terminalId'] ?? $_GET['id'] ?? null;

        if ($terminalId === null || $terminalId === '') {
            throw new \RuntimeException('Missing terminalId.');
        }

        $accessToken = $this->getAccessToken();

        $status = $this->apiClient->getSmartTerminalConnectionStatus(
            accessToken: $accessToken->accessToken,
            terminalId: $terminalId,
        );

        $this->jsonResponse([
            'status' => $status,
        ]);
    }

    public function getInternalTerminal()
    {
        $terminalId = $_GET['terminalId'] ?? $_GET['id'] ?? null;

        if ($terminalId === null || $terminalId === '') {
            throw new \RuntimeException('Missing terminalId.');
        }

        $accessToken = $this->getAccessToken();

        $terminal = $this->apiClient->getInternalTerminal(
            accessToken: $accessToken->accessToken,
            terminalId: $terminalId,
        );

        $this->jsonResponse([
            'terminal' => $terminal,
        ]);
    }

    public function updateInternalTerminal()
    {
        $terminalId = $_GET['terminalId'] ?? $_GET['id'] ?? null;

        if ($terminalId === null || $terminalId === '') {
            throw new \RuntimeException('Missing terminalId.');
        }

        $accessToken = $this->getAccessToken();

        $terminal = $this->apiClient->updateInternalTerminal(
            accessToken: $accessToken->accessToken,
            terminalId: $terminalId,
            terminal: [
                'name' => $_GET['name'] ?? null,
                'receiptSettings' => [
                    'receiptName' => $_GET['receiptName'] ?? null,
                    'receiptLocation' => $_GET['receiptLocation'] ?? null,
                    'qrReceiptEnabled' => isset($_GET['qrReceiptEnabled'])
                        ? filter_var($_GET['qrReceiptEnabled'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
                        : null,
                    'customerReceiptMode' => $_GET['customerReceiptMode'] ?? null,
                    'merchantReceiptMode' => $_GET['merchantReceiptMode'] ?? null,
                ],
                'paymentSettings' => [
                    'bankText' => $_GET['bankText'] ?? null,
                    'paymentReferenceEnabled' => isset($_GET['paymentReferenceEnabled'])
                        ? filter_var($_GET['paymentReferenceEnabled'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
                        : null,
                    'limitedAcceptanceEnabled' => isset($_GET['limitedAcceptanceEnabled'])
                        ? filter_var($_GET['limitedAcceptanceEnabled'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
                        : null,
                    'amexEnabled' => isset($_GET['amexEnabled'])
                        ? filter_var($_GET['amexEnabled'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
                        : null,
                    'refundEnabled' => isset($_GET['refundEnabled'])
                        ? filter_var($_GET['refundEnabled'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
                        : null,
                    'refundPassword' => $_GET['refundPassword'] ?? null,
                    'terminalLoginOptions' => $_GET['terminalLoginOptions'] ?? null,
                    'fixedAmountMode' => $_GET['fixedAmountMode'] ?? null,
                ],
                'wecrSettings' => [
                    'wecrEnabled' => isset($_GET['wecrEnabled'])
                        ? filter_var($_GET['wecrEnabled'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
                        : null,
                    'wecrSupplier' => $_GET['wecrSupplier'] ?? null,
                    'wecrLogin' => $_GET['wecrLogin'] ?? null,
                ],
            ],
        );

        $this->jsonResponse([
            'terminal' => $terminal,
        ]);
    }

    public function getInternalTerminalConnectionStatus()
    {
        $terminalId = $_GET['terminalId'] ?? $_GET['id'] ?? null;

        if ($terminalId === null || $terminalId === '') {
            throw new \RuntimeException('Missing terminalId.');
        }

        $accessToken = $this->getAccessToken();

        $status = $this->apiClient->getInternalTerminalConnectionStatus(
            accessToken: $accessToken->accessToken,
            terminalId: $terminalId,
        );

        $this->jsonResponse([
            'status' => $status,
        ]);
    }

    public function cancelInternalTerminalAction()
    {
        $terminalId = $_GET['terminalId'] ?? $_GET['id'] ?? null;

        if ($terminalId === null || $terminalId === '') {
            throw new \RuntimeException('Missing terminalId.');
        }

        $accessToken = $this->getAccessToken();

        $this->apiClient->cancelInternalTerminalAction(
            accessToken: $accessToken->accessToken,
            terminalId: $terminalId,
        );

        $this->jsonResponse([
            'cancelled' => true,
            'terminalId' => $terminalId,
        ]);
    }

    public function createReport()
    {
        $reportDefinitionId = $_GET['reportDefinitionId'] ?? null;

        if ($reportDefinitionId === null || $reportDefinitionId === '') {
            throw new \RuntimeException('Missing reportDefinitionId.');
        }

        $recipientAddress = $_GET['recipientAddress'] ?? $_GET['email'] ?? null;

        if ($recipientAddress === null || $recipientAddress === '') {
            throw new \RuntimeException('Missing recipientAddress.');
        }

        $filters = [];
        if (isset($_GET['filters'])) {
            $decodedFilters = json_decode($_GET['filters'], true);
            if (!is_array($decodedFilters)) {
                throw new \RuntimeException('Invalid filters JSON.');
            }
            $filters = $decodedFilters;
        }

        $accessToken = $this->getAccessToken();

        $report = $this->apiClient->createReport(
            accessToken: $accessToken->accessToken,
            report: [
                'reportDefinitionId' => $reportDefinitionId,
                'scope' => $_GET['scope'] ?? 'Merchant',
                'storeId' => $_GET['storeId'] ?? null,
                'filters' => $filters,
                'user' => $_GET['user'] ?? null,
                'recipients' => [
                    [
                        'notificationChannel' => $_GET['notificationChannel'] ?? 'Mail',
                        'notificationAddress' => $recipientAddress,
                        'recipientName' => $_GET['recipientName'] ?? null,
                    ],
                ],
            ],
        );

        $this->jsonResponse([
            'report' => $report,
        ]);
    }

    public function getReport()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing report id.');
        }

        $accessToken = $this->getAccessToken();

        $report = $this->apiClient->getReport(
            accessToken: $accessToken->accessToken,
            id: $id,
        );

        $this->jsonResponse([
            'report' => $report,
        ]);
    }

    private function searchAllReports(
        string $accessToken,
        array $filters = [],
    ): array {
        $allReports = [];
        $continuationToken = null;

        do {
            $filtersWithContinuation = $filters;

            if ($continuationToken !== null && $continuationToken !== '') {
                $filtersWithContinuation['continuationToken'] = $continuationToken;
            }

            $result = $this->apiClient->searchReports(
                accessToken: $accessToken,
                filters: $filtersWithContinuation,
            );

            $allReports = array_merge($allReports, $result->results);
            $continuationToken = $result->nextContinuationToken;
        } while ($continuationToken !== null && $continuationToken !== '');

        return $allReports;
    }

    public function searchReports()
    {
        $accessToken = $this->getAccessToken();

        $reports = $this->searchAllReports(
            accessToken: $accessToken->accessToken,
            filters: [
                'user' => $_GET['user'] ?? null,
                'type' => $_GET['type'] ?? null,
                'status' => $_GET['status'] ?? null,
                'dateFrom' => $_GET['dateFrom'] ?? null,
                'dateTo' => $_GET['dateTo'] ?? null,
                'limit' => self::SEARCH_LIMIT,
            ],
        );

        $this->jsonResponse([
            'count' => count($reports),
            'reports' => $reports,
        ]);
    }

    public function getReportDefinitions()
    {
        $accessToken = $this->getAccessToken();

        $result = $this->apiClient->getReportDefinitions(
            accessToken: $accessToken->accessToken,
        );

        $this->jsonResponse([
            'count' => count($result->reportDefinitions),
            'reportDefinitions' => $result->reportDefinitions,
        ]);
    }

    public function searchReportDefinitions()
    {
        $accessToken = $this->getAccessToken();

        $result = $this->apiClient->searchReportDefinitions(
            accessToken: $accessToken->accessToken,
        );

        $this->jsonResponse([
            'count' => count($result->reportDefinitions),
            'reportDefinitions' => $result->reportDefinitions,
        ]);
    }

    public function createReportSchedule()
    {
        $reportDefinitionId = $_GET['reportDefinitionId'] ?? null;

        if ($reportDefinitionId === null || $reportDefinitionId === '') {
            throw new \RuntimeException('Missing reportDefinitionId.');
        }

        $recipientAddress = $_GET['recipientAddress'] ?? $_GET['email'] ?? null;

        if ($recipientAddress === null || $recipientAddress === '') {
            throw new \RuntimeException('Missing recipientAddress.');
        }

        $filters = [];
        if (isset($_GET['filters'])) {
            $decodedFilters = json_decode($_GET['filters'], true);
            if (!is_array($decodedFilters)) {
                throw new \RuntimeException('Invalid filters JSON.');
            }
            $filters = $decodedFilters;
        }

        $accessToken = $this->getAccessToken();

        $schedule = $this->apiClient->createReportSchedule(
            accessToken: $accessToken->accessToken,
            schedule: [
                'reportDefinitionId' => $reportDefinitionId,
                'storeId' => $_GET['storeId'] ?? null,
                'status' => $_GET['status'] ?? 'Active',
                'interval' => $_GET['interval'] ?? 'Daily',
                'scope' => $_GET['scope'] ?? 'Merchant',
                'filters' => $filters,
                'recipients' => [
                    [
                        'notificationChannel' => $_GET['notificationChannel'] ?? 'Mail',
                        'notificationAddress' => $recipientAddress,
                        'recipientName' => $_GET['recipientName'] ?? null,
                    ],
                ],
            ],
        );

        $this->jsonResponse([
            'schedule' => $schedule,
        ]);
    }

    public function updateReportSchedule()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing report schedule id.');
        }

        $reportDefinitionId = $_GET['reportDefinitionId'] ?? null;

        if ($reportDefinitionId === null || $reportDefinitionId === '') {
            throw new \RuntimeException('Missing reportDefinitionId.');
        }

        $recipientAddress = $_GET['recipientAddress'] ?? $_GET['email'] ?? null;

        if ($recipientAddress === null || $recipientAddress === '') {
            throw new \RuntimeException('Missing recipientAddress.');
        }

        $filters = [];
        if (isset($_GET['filters'])) {
            $decodedFilters = json_decode($_GET['filters'], true);
            if (!is_array($decodedFilters)) {
                throw new \RuntimeException('Invalid filters JSON.');
            }
            $filters = $decodedFilters;
        }

        $accessToken = $this->getAccessToken();

        $schedule = $this->apiClient->updateReportSchedule(
            accessToken: $accessToken->accessToken,
            id: $id,
            schedule: [
                'reportDefinitionId' => $reportDefinitionId,
                'storeId' => $_GET['storeId'] ?? null,
                'status' => $_GET['status'] ?? 'Active',
                'interval' => $_GET['interval'] ?? 'Daily',
                'scope' => $_GET['scope'] ?? 'Merchant',
                'filters' => $filters,
                'recipients' => [
                    [
                        'notificationChannel' => $_GET['notificationChannel'] ?? 'Mail',
                        'notificationAddress' => $recipientAddress,
                        'recipientName' => $_GET['recipientName'] ?? null,
                    ],
                ],
            ],
        );

        $this->jsonResponse([
            'schedule' => $schedule,
        ]);
    }

    public function getReportSchedule()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing report schedule id.');
        }

        $accessToken = $this->getAccessToken();

        $schedule = $this->apiClient->getReportSchedule(
            accessToken: $accessToken->accessToken,
            id: $id,
        );

        $this->jsonResponse([
            'schedule' => $schedule,
        ]);
    }

    public function deleteReportSchedule()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing report schedule id.');
        }

        $accessToken = $this->getAccessToken();

        $this->apiClient->deleteReportSchedule(
            accessToken: $accessToken->accessToken,
            id: $id,
        );

        $this->jsonResponse([
            'deleted' => true,
            'id' => $id,
        ]);
    }

    private function searchAllReportSchedules(
        string $accessToken,
        array $filters = [],
    ): array {
        $allSchedules = [];
        $continuationToken = null;

        do {
            $filtersWithContinuation = $filters;

            if ($continuationToken !== null && $continuationToken !== '') {
                $filtersWithContinuation['continuationToken'] = $continuationToken;
            }

            $result = $this->apiClient->searchReportSchedules(
                accessToken: $accessToken,
                filters: $filtersWithContinuation,
            );

            $allSchedules = array_merge($allSchedules, $result->results);
            $continuationToken = $result->nextContinuationToken;
        } while ($continuationToken !== null && $continuationToken !== '');

        return $allSchedules;
    }

    public function searchReportSchedules()
    {
        $accessToken = $this->getAccessToken();

        $schedules = $this->searchAllReportSchedules(
            accessToken: $accessToken->accessToken,
            filters: [
                'limit' => isset($_GET['limit']) ? (int) $_GET['limit'] : self::SEARCH_LIMIT,
            ],
        );

        $this->jsonResponse([
            'count' => count($schedules),
            'schedules' => $schedules,
        ]);
    }

    public function createSale()
    {
        $accessToken = $this->getAccessToken();

        $reference = $_GET['reference'] ?? 'sale-' . date('YmdHis');
        $amount = $_GET['amount'] ?? $_GET['totalAmount'] ?? '1.00';

        $sale = $this->apiClient->createSale(
            accessToken: $accessToken->accessToken,
            sale: [
                'storeId' => $_GET['storeId'] ?? null,
                'reference' => $reference,
                'currency' => $_GET['currency'] ?? 'EUR',
                'totalAmount' => $amount,
                'breakdown' => [
                    'baseAmount' => $_GET['baseAmount'] ?? $amount,
                    'vatAmount' => $_GET['vatAmount'] ?? '0.00',
                    'shippingFee' => $_GET['shippingFee'] ?? '0.00',
                    'handlingFee' => $_GET['handlingFee'] ?? '0.00',
                    'discountAmount' => $_GET['discountAmount'] ?? '0.00',
                ],
                'description' => $_GET['description'] ?? 'Test sale from Buckaroo SDK',
                'locale' => $_GET['locale'] ?? 'nl-NL',
                'sequenceType' => $_GET['sequenceType'] ?? 'OneOff',
                'intentType' => $_GET['intentType'] ?? 'Pay',
                'statementDescriptor' => $_GET['statementDescriptor'] ?? null,
                'paymentMethods' => isset($_GET['paymentMethod'])
                    ? [$_GET['paymentMethod']]
                    : (isset($_GET['terminalId']) ? ['Pos'] : null),
                'paymentMethodOptions' => [
                    'pos' => [
                        'terminalId' => $_GET['terminalId'] ?? null,
                    ],
                ],
                'returnUrl' => $_GET['returnUrl'] ?? null,
                'cancelUrl' => $_GET['cancelUrl'] ?? null,
                'customer' => [
                    'reference' => $_GET['customerReference'] ?? $reference,
                    'email' => $_GET['email'] ?? null,
                    'givenName' => $_GET['givenName'] ?? null,
                    'familyName' => $_GET['familyName'] ?? null,
                    'preferredLocale' => $_GET['preferredLocale'] ?? 'nl-NL',
                ],
                'metadata' => [
                    'source' => 'test-controller',
                ],
            ],
        );

        $this->jsonResponse([
            'sale' => $sale,
        ]);
    }

    private function searchAllSales(
        string $accessToken,
        array $filters = [],
    ): array {
        $allSales = [];
        $continuationToken = null;

        do {
            $filtersWithContinuation = $filters;

            if ($continuationToken !== null && $continuationToken !== '') {
                $filtersWithContinuation['continuationToken'] = $continuationToken;
            }

            $result = $this->apiClient->searchSales(
                accessToken: $accessToken,
                filters: $filtersWithContinuation,
            );

            $allSales = array_merge($allSales, $result->sales);
            $continuationToken = $result->nextContinuationToken;
        } while ($continuationToken !== null && $continuationToken !== '');

        return $allSales;
    }

    public function searchSales()
    {
        $accessToken = $this->getAccessToken();

        $filters = [
            'storeIds' => isset($_GET['storeId']) ? [$_GET['storeId']] : null,
            'terminalIds' => isset($_GET['terminalId']) ? [$_GET['terminalId']] : null,
            'paymentMethodCodes' => isset($_GET['paymentMethodCode']) ? [$_GET['paymentMethodCode']] : null,
            'statuses' => isset($_GET['status']) ? [$_GET['status']] : null,
            'sequenceTypes' => isset($_GET['sequenceType']) ? [$_GET['sequenceType']] : null,
            'merchantReferences' => isset($_GET['merchantReference']) ? [$_GET['merchantReference']] : null,
            'fullText' => $_GET['fullText'] ?? null,
            'createdFrom' => $_GET['createdFrom'] ?? date('c', strtotime('-7 days')),
            'createdTo' => $_GET['createdTo'] ?? date('c'),
            'minAmount' => $_GET['minAmount'] ?? null,
            'maxAmount' => $_GET['maxAmount'] ?? null,
            'limit' => self::SEARCH_LIMIT,
        ];

        $sales = $this->searchAllSales(
            accessToken: $accessToken->accessToken,
            filters: $filters,
        );

        $this->jsonResponse([
            'count' => count($sales),
            'sales' => $sales,
        ]);
    }

    private function searchAllTransactions(
        string $accessToken,
        string $fromDateTime,
        string $toDateTime,
        array $filters = [],
    ): array
    {
        $allTransactions = [];
        $continuationToken = null;

        do {
            $filters = [
                'fromDateTime' => $fromDateTime,
                'toDateTime' => $toDateTime,
                'limit' => self::SEARCH_LIMIT,
            ] + $filters;

            if ($continuationToken !== null && $continuationToken !== '') {
                $filters['continuationToken'] = $continuationToken;
            }

            $result = $this->apiClient->searchTransactions(
                accessToken: $accessToken,
                filters: $filters,
            );

            $allTransactions = array_merge($allTransactions, $result->transactions);
            $continuationToken = $result->nextContinuationToken;
        } while ($continuationToken !== null && $continuationToken !== '');

        return $allTransactions;
    }

    public function searchTransactions()
    {
        $accessToken = $this->getAccessToken();

        $filters = [
            'storeIds' => isset($_GET['storeId'])
                ? [$_GET['storeId']]
                : null,
            'terminalIds' => isset($_GET['terminalId'])
                ? [$_GET['terminalId']]
                : null,
            'statuses' => isset($_GET['status'])
                ? [$_GET['status']]
                : null,
            'paymentMethodCodes' => isset($_GET['paymentMethodCode'])
                ? [$_GET['paymentMethodCode']]
                : null,
            'merchantReferences' => isset($_GET['merchantReference'])
                ? [$_GET['merchantReference']]
                : null,
        ];

        $transactions = $this->searchAllTransactions(
            accessToken: $accessToken->accessToken,
            fromDateTime: $_GET['fromDateTime'] ?? date('c', strtotime('-7 days')),
            toDateTime: $_GET['toDateTime'] ?? date('c'),
            filters: $filters,
        );

        $this->jsonResponse([
            'count' => count($transactions),
            'transactions' => $transactions,
        ]);
    }

    public function getTransaction()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing transaction id.');
        }

        $accessToken = $this->getAccessToken();

        $transaction = $this->apiClient->getTransaction(
            accessToken: $accessToken->accessToken,
            id: $id,
        );

        $this->jsonResponse([
            'transaction' => $transaction,
        ]);
    }

    public function getSale()
    {
        $saleId = $_GET['saleId'] ?? $_GET['id'] ?? null;

        if ($saleId === null || $saleId === '') {
            throw new \RuntimeException('Missing saleId.');
        }

        $accessToken = $this->getAccessToken();

        $sale = $this->apiClient->getSale(
            accessToken: $accessToken->accessToken,
            saleId: $saleId,
        );

        $this->jsonResponse([
            'sale' => $sale,
        ]);
    }

    public function cancelSale()
    {
        $saleId = $_GET['saleId'] ?? $_GET['id'] ?? null;

        if ($saleId === null || $saleId === '') {
            throw new \RuntimeException('Missing saleId.');
        }

        $accessToken = $this->getAccessToken();

        $sale = $this->apiClient->cancelSale(
            accessToken: $accessToken->accessToken,
            saleId: $saleId,
        );

        $this->jsonResponse([
            'sale' => $sale,
        ]);
    }

    public function getSaleByReference()
    {
        $reference = $_GET['reference'] ?? null;

        if ($reference === null || $reference === '') {
            throw new \RuntimeException('Missing sale reference.');
        }

        $accessToken = $this->getAccessToken();

        $sale = $this->apiClient->getSaleByReference(
            accessToken: $accessToken->accessToken,
            reference: $reference,
        );

        $this->jsonResponse([
            'sale' => $sale,
        ]);
    }

    public function globalSearch()
    {
        $needle = $_GET['needle'] ?? $_GET['q'] ?? null;

        if ($needle === null || $needle === '') {
            throw new \RuntimeException('Missing needle.');
        }

        $accessToken = $this->getAccessToken();

        $result = $this->apiClient->search(
            accessToken: $accessToken->accessToken,
            needle: $needle,
            resourceType: $_GET['resourceType'] ?? null,
            limit: isset($_GET['limit']) ? (int) $_GET['limit'] : self::SEARCH_LIMIT,
        );

        $this->jsonResponse([
            'count' => count($result->results),
            'results' => $result->results,
        ]);
    }

    public function searchServiceSubscriptions()
    {
        $accessToken = $this->getAccessToken();

        $result = $this->apiClient->searchServiceSubscriptions(
            accessToken: $accessToken->accessToken,
            filters: [
                'serviceCode' => $_GET['serviceCode'] ?? null,
                'statuses' => isset($_GET['status']) ? [$_GET['status']] : null,
            ],
        );

        $this->jsonResponse([
            'count' => count($result->subscriptions),
            'subscriptions' => $result->subscriptions,
        ]);
    }

    public function getServiceSubscription()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing service subscription id.');
        }

        $accessToken = $this->getAccessToken();

        $subscription = $this->apiClient->getServiceSubscription(
            accessToken: $accessToken->accessToken,
            id: $id,
        );

        $this->jsonResponse([
            'subscription' => $subscription,
        ]);
    }

    public function updateServiceSubscription()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing service subscription id.');
        }

        $action = $_GET['action'] ?? null;

        if ($action === null || $action === '') {
            throw new \RuntimeException('Missing service subscription action.');
        }

        $accessToken = $this->getAccessToken();

        $subscription = $this->apiClient->updateServiceSubscription(
            accessToken: $accessToken->accessToken,
            id: $id,
            action: $action,
        );

        $this->jsonResponse([
            'subscription' => $subscription,
        ]);
    }

    public function reprioritiseServiceSubscriptions()
    {
        $code = $_GET['code'] ?? $_GET['serviceCode'] ?? null;

        if ($code === null || $code === '') {
            throw new \RuntimeException('Missing service code.');
        }

        $orderedSubscriptionIds = [];

        if (isset($_GET['orderedSubscriptionIds'])) {
            $orderedSubscriptionIds = array_values(array_filter(array_map('trim', explode(',', $_GET['orderedSubscriptionIds']))));
        } elseif (isset($_GET['ids'])) {
            $orderedSubscriptionIds = array_values(array_filter(array_map('trim', explode(',', $_GET['ids']))));
        }

        if ($orderedSubscriptionIds === []) {
            throw new \RuntimeException('Missing orderedSubscriptionIds.');
        }

        $accessToken = $this->getAccessToken();

        $result = $this->apiClient->reprioritiseServiceSubscriptions(
            accessToken: $accessToken->accessToken,
            code: $code,
            orderedSubscriptionIds: $orderedSubscriptionIds,
        );

        $this->jsonResponse([
            'count' => count($result->subscriptions),
            'subscriptions' => $result->subscriptions,
        ]);
    }

    private function getAllStores(
        string $accessToken,
        ?string $status = null,
    ): array {
        $allStores = [];
        $continuationToken = null;

        do {
            $result = $this->apiClient->getStores(
                accessToken: $accessToken,
                status: $status,
                continuationToken: $continuationToken,
            );

            $allStores = array_merge($allStores, $result->stores);
            $continuationToken = $result->nextContinuationToken;
        } while ($continuationToken !== null && $continuationToken !== '');

        return $allStores;
    }

    public function getStores()
    {
        $accessToken = $this->getAccessToken();

        $stores = $this->getAllStores(
            accessToken: $accessToken->accessToken,
            status: $_GET['status'] ?? null,
        );

        $this->jsonResponse([
            'count' => count($stores),
            'stores' => $stores,
        ]);
    }

    public function createStore()
    {
        $accessToken = $this->getAccessToken();

        $store = $this->apiClient->createStore(
            accessToken: $accessToken->accessToken,
            store: [
                'type' => $_GET['type'] ?? 'Online',
                'name' => $_GET['name'] ?? null,
                'description' => $_GET['description'] ?? null,
                'url' => $_GET['url'] ?? null,
                'status' => $_GET['status'] ?? 'Test',
                'address' => [
                    'street' => $_GET['street'] ?? null,
                    'houseNumber' => $_GET['houseNumber'] ?? null,
                    'postalCode' => $_GET['postalCode'] ?? null,
                    'city' => $_GET['city'] ?? null,
                    'country' => $_GET['country'] ?? 'NL',
                ],
            ],
        );

        $this->jsonResponse([
            'store' => $store,
        ]);
    }

    private function searchAllStores(
        string $accessToken,
        array $filters = [],
    ): array {
        $allStores = [];
        $continuationToken = null;

        do {
            $filtersWithContinuation = $filters;

            if ($continuationToken !== null && $continuationToken !== '') {
                $filtersWithContinuation['continuationToken'] = $continuationToken;
            }

            $result = $this->apiClient->searchStores(
                accessToken: $accessToken,
                filters: $filtersWithContinuation,
            );

            $allStores = array_merge($allStores, $result->stores);
            $continuationToken = $result->nextContinuationToken;
        } while ($continuationToken !== null && $continuationToken !== '');

        return $allStores;
    }

    public function searchStores()
    {
        $accessToken = $this->getAccessToken();

        $filters = [
            'statuses' => isset($_GET['status']) ? [$_GET['status']] : null,
            'types' => isset($_GET['type']) ? [$_GET['type']] : null,
            'mcc' => $_GET['mcc'] ?? null,
        ];

        $stores = $this->searchAllStores(
            accessToken: $accessToken->accessToken,
            filters: $filters,
        );

        $this->jsonResponse([
            'count' => count($stores),
            'stores' => $stores,
        ]);
    }

    public function updateStore()
    {
        $storeId = $_GET['storeId'] ?? $_GET['id'] ?? null;

        if ($storeId === null || $storeId === '') {
            throw new \RuntimeException('Missing storeId.');
        }

        $accessToken = $this->getAccessToken();

        $store = $this->apiClient->updateStore(
            accessToken: $accessToken->accessToken,
            storeId: $storeId,
            store: [
                'type' => $_GET['type'] ?? null,
                'name' => $_GET['name'] ?? null,
                'description' => $_GET['description'] ?? null,
                'url' => $_GET['url'] ?? null,
                'status' => $_GET['status'] ?? null,
                'address' => [
                    'street' => $_GET['street'] ?? null,
                    'houseNumber' => $_GET['houseNumber'] ?? null,
                    'postalCode' => $_GET['postalCode'] ?? null,
                    'city' => $_GET['city'] ?? null,
                    'country' => $_GET['country'] ?? null,
                ],
            ],
        );

        $this->jsonResponse([
            'store' => $store,
        ]);
    }

    public function getWebhookEventTypes()
    {
        $accessToken = $this->getAccessToken();

        $result = $this->apiClient->getWebhookEventTypes(
            accessToken: $accessToken->accessToken,
        );

        $this->jsonResponse([
            'count' => count($result->eventTypes),
            'eventTypes' => $result->eventTypes,
        ]);
    }

    private function searchAllWebhooks(
        string $accessToken,
        array $filters = [],
    ): array {
        $allWebhooks = [];
        $continuationToken = null;

        do {
            $filtersWithContinuation = $filters;

            if ($continuationToken !== null && $continuationToken !== '') {
                $filtersWithContinuation['continuationToken'] = $continuationToken;
            }

            $result = $this->apiClient->searchWebhooks(
                accessToken: $accessToken,
                filters: $filtersWithContinuation,
            );

            $allWebhooks = array_merge($allWebhooks, $result->webhooks);
            $continuationToken = $result->nextContinuationToken;
        } while ($continuationToken !== null && $continuationToken !== '');

        return $allWebhooks;
    }

    public function searchWebhooks()
    {
        $accessToken = $this->getAccessToken();

        $storeIds = null;
        if (isset($_GET['storeIds'])) {
            $storeIds = array_values(array_filter(array_map('trim', explode(',', $_GET['storeIds']))));
        } elseif (isset($_GET['storeId'])) {
            $storeIds = [$_GET['storeId']];
        }

        $eventTypes = null;
        if (isset($_GET['eventTypes'])) {
            $eventTypes = array_values(array_filter(array_map('trim', explode(',', $_GET['eventTypes']))));
        } elseif (isset($_GET['eventType'])) {
            $eventTypes = [$_GET['eventType']];
        }

        $webhooks = $this->searchAllWebhooks(
            accessToken: $accessToken->accessToken,
            filters: [
                'storeIds' => $storeIds,
                'eventTypes' => $eventTypes,
                'limit' => self::SEARCH_LIMIT,
            ],
        );

        $this->jsonResponse([
            'count' => count($webhooks),
            'webhooks' => $webhooks,
        ]);
    }

    public function createWebhook()
    {
        $url = $_GET['url'] ?? null;
        $secret = $_GET['secret'] ?? null;

        if ($url === null || $url === '') {
            throw new \RuntimeException('Missing webhook url.');
        }

        if ($secret === null || $secret === '') {
            throw new \RuntimeException('Missing webhook secret.');
        }

        $eventTypes = [];
        if (isset($_GET['eventTypes'])) {
            $eventTypes = array_values(array_filter(array_map('trim', explode(',', $_GET['eventTypes']))));
        } elseif (isset($_GET['eventType'])) {
            $eventTypes = [$_GET['eventType']];
        }

        if ($eventTypes === []) {
            throw new \RuntimeException('Missing webhook eventTypes.');
        }

        $accessToken = $this->getAccessToken();

        $webhook = $this->apiClient->createWebhook(
            accessToken: $accessToken->accessToken,
            webhook: [
                'url' => $url,
                'storeId' => $_GET['storeId'] ?? null,
                'eventTypes' => $eventTypes,
                'maxConcurrency' => isset($_GET['maxConcurrency']) ? (int) $_GET['maxConcurrency'] : 10,
                'secret' => $secret,
            ],
        );

        $this->jsonResponse([
            'webhook' => $webhook,
        ]);
    }

    public function getWebhook()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing webhook id.');
        }

        $accessToken = $this->getAccessToken();

        $webhook = $this->apiClient->getWebhook(
            accessToken: $accessToken->accessToken,
            id: $id,
        );

        $this->jsonResponse([
            'webhook' => $webhook,
        ]);
    }

    public function updateWebhook()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing webhook id.');
        }

        $eventTypes = [];
        if (isset($_GET['eventTypes'])) {
            $eventTypes = array_values(array_filter(array_map('trim', explode(',', $_GET['eventTypes']))));
        } elseif (isset($_GET['eventType'])) {
            $eventTypes = [$_GET['eventType']];
        }

        if ($eventTypes === []) {
            throw new \RuntimeException('Missing webhook eventTypes.');
        }

        $accessToken = $this->getAccessToken();

        $webhook = $this->apiClient->updateWebhook(
            accessToken: $accessToken->accessToken,
            id: $id,
            payload: [
                'url' => $_GET['url'] ?? null,
                'maxConcurrency' => isset($_GET['maxConcurrency']) ? (int) $_GET['maxConcurrency'] : null,
                'eventTypes' => $eventTypes,
                'status' => $_GET['status'] ?? null,
            ],
        );

        $this->jsonResponse([
            'webhook' => $webhook,
        ]);
    }

    public function deleteWebhook()
    {
        $id = $_GET['id'] ?? null;

        if ($id === null || $id === '') {
            throw new \RuntimeException('Missing webhook id.');
        }

        $accessToken = $this->getAccessToken();

        $this->apiClient->deleteWebhook(
            accessToken: $accessToken->accessToken,
            id: $id,
        );

        $this->jsonResponse([
            'deleted' => true,
            'id' => $id,
        ]);
    }

    private function jsonResponse(array $data): void
    {
        header('Content-Type: application/json');

        echo json_encode($data, JSON_PRETTY_PRINT);
    }
}

统计信息

  • 总下载量: 1
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 1
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-05-29

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固