mirrorps/yii2-taler

Yii2 extension for GNU Taler REST API integration

Maintainers

Package info

github.com/mirrorps/yii2-taler

Type:yii2-extension

pkg:composer/mirrorps/yii2-taler

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main 2026-04-27 15:16 UTC

This package is auto-updated.

Last update: 2026-04-27 15:40:45 UTC


README

Notice: This package is under active development, and the code may change.

Yii2 extension for GNU Taler REST API integration.

Installation

composer require mirrorps/yii2-taler

Configuration

Add the Taler component to your application configuration:

'components' => [
    'taler' => [
        'class'   => \mirrorps\Yii2Taler\Taler::class,
        'baseUrl' => 'https://backend.demo.taler.net/instances/sandbox',
        'token'   => 'Bearer secret-token:sandbox',
        // OR credential-based auth:
        // 'username' => 'merchant',
        // 'password' => 'secret',
        // 'instance' => 'sandbox',
    ],
],

Config API

The Config API is accessible via Yii::$app->taler->configs().

Get Merchant Config

use Taler\Api\Config\Dto\MerchantVersionResponse;

$config = Yii::$app->taler->configs()->getConfig();

if ($config instanceof MerchantVersionResponse) {
    echo $config->name . PHP_EOL;      // taler-merchant
    echo $config->version . PHP_EOL;   // libtool format current:revision:age
    echo $config->currency . PHP_EOL;  // default currency

    foreach ($config->currencies as $code => $spec) {
        echo $code . ' => ' . $spec->name . PHP_EOL;
    }

    foreach ($config->exchanges as $exchange) {
        echo $exchange->base_url . PHP_EOL;
    }
}

Async Config Call

$promise = Yii::$app->taler->configs()->getConfigAsync();
$config = $promise->wait();

Credential Health Check

$report = Yii::$app->taler->configCheck();

if ($report['ok']) {
    echo "All checks passed" . PHP_EOL;
} else {
    print_r($report);
}

Order API

The Order API is accessible via Yii::$app->taler->orders().

List Orders

use Taler\Api\Order\Dto\GetOrdersRequest;

// List all orders (default limit)
$orderHistory = Yii::$app->taler->orders()->getOrders();

foreach ($orderHistory->orders as $entry) {
    echo $entry->order_id . '' . $entry->summary . '' . $entry->amount . PHP_EOL;
}

// With filters
$orderHistory = Yii::$app->taler->orders()->getOrders(
    new GetOrdersRequest(paid: true, limit: 10)
);

Get Order Details

use Taler\Api\Order\Dto\GetOrderRequest;
use Taler\Api\Order\Dto\CheckPaymentPaidResponse;
use Taler\Api\Order\Dto\CheckPaymentUnpaidResponse;
use Taler\Api\Order\Dto\CheckPaymentClaimedResponse;

$order = Yii::$app->taler->orders()->getOrder('my-order-id-2025-1');

if ($order instanceof CheckPaymentPaidResponse) {
    echo 'Paid! Deposit total: ' . $order->deposit_total . PHP_EOL;
} elseif ($order instanceof CheckPaymentUnpaidResponse) {
    echo 'Unpaid. Payment URI: ' . $order->taler_pay_uri . PHP_EOL;
} elseif ($order instanceof CheckPaymentClaimedResponse) {
    echo 'Claimed by wallet.' . PHP_EOL;
}

Create Order

use Taler\Api\Order\Dto\Amount;
use Taler\Api\Order\Dto\OrderV0;
use Taler\Api\Order\Dto\PostOrderRequest;

$order = new OrderV0(
    summary: 'T-Shirt (GNU Taler demo)',
    amount: new Amount('KUDOS:10.00'),
    fulfillment_message: 'Thank you for purchasing a T-Shirt!',
);

$response = Yii::$app->taler->orders()->createOrder(
    new PostOrderRequest(order: $order)
);

echo 'Created order: ' . $response->order_id . PHP_EOL;
if ($response->token !== null) {
    echo 'Claim token: ' . $response->token . PHP_EOL;
}

Refund Order

use Taler\Api\Order\Dto\RefundRequest;

$refundResponse = Yii::$app->taler->orders()->refundOrder(
    'my-order-id-2025-1',
    new RefundRequest(
        refund: 'KUDOS:5.00',
        reason: 'Customer requested refund',
    )
);

echo 'Refund URI: ' . $refundResponse->taler_refund_uri . PHP_EOL;

Delete Order

Yii::$app->taler->orders()->deleteOrder('my-order-id-2025-1');
echo 'Order deleted.' . PHP_EOL;

Forget Order Fields

use Taler\Api\Order\Dto\ForgetRequest;

Yii::$app->taler->orders()->forgetOrder(
    'my-order-id-2025-1',
    new ForgetRequest(fields: ['$.delivery_location'])
);

echo 'Fields forgotten.' . PHP_EOL;

Instance API

The Instance API is accessible via Yii::$app->taler->instances().

List Instances

$list = Yii::$app->taler->instances()->getInstances();

foreach ($list->instances as $instance) {
    echo $instance->id . '' . $instance->name . PHP_EOL;
}

NOTE: If your backend returns 404, you are likely using a per-instance base URL such as https://backend.demo.taler.net/instances/sandbox. In that setup, use the single-instance private endpoint instead: GET https://backend.demo.taler.net/instances/sandbox/private

Get Instance Details

$details = Yii::$app->taler->instances()->getInstance('shop-1');

echo $details->name;         // e.g., "My Shop"
echo $details->merchant_pub; // EddsaPublicKey

Create Instance

use Taler\Api\Instance\Dto\InstanceConfigurationMessage;
use Taler\Api\Instance\Dto\InstanceAuthConfigToken;
use Taler\Api\Dto\Location;
use Taler\Api\Dto\RelativeTime;

$config = new InstanceConfigurationMessage(
    id: 'shop-1',
    name: 'My Shop',
    auth: new InstanceAuthConfigToken(password: 'super-secret'),
    address: new Location(country: 'DE', town: 'Berlin'),
    jurisdiction: new Location(country: 'DE', town: 'Berlin'),
    use_stefan: true,
    default_wire_transfer_delay: new RelativeTime(3600_000_000),
    default_pay_delay: new RelativeTime(300_000_000),
    email: 'merchant@example.com',
);

Yii::$app->taler->instances()->createInstance($config);

Update Instance

use Taler\Api\Instance\Dto\InstanceReconfigurationMessage;
use Taler\Api\Dto\Location;
use Taler\Api\Dto\RelativeTime;

$patch = new InstanceReconfigurationMessage(
    name: 'My Shop GmbH',
    address: new Location(country: 'DE', town: 'Berlin'),
    jurisdiction: new Location(country: 'DE', town: 'Berlin'),
    use_stefan: true,
    default_wire_transfer_delay: new RelativeTime(7200_000_000),
    default_pay_delay: new RelativeTime(600_000_000),
    website: 'https://shop.example.com',
);

Yii::$app->taler->instances()->updateInstance('shop-1', $patch);

Delete Instance

use Taler\Api\TwoFactorAuth\Dto\ChallengeResponse;

// Disable instance
$result = Yii::$app->taler->instances()->deleteInstance('shop-1');

if ($result instanceof ChallengeResponse) {
    echo '2FA required. Challenge ID: ' . $result->getChallengeId() . PHP_EOL;
}

// Purge instance (irreversible)
Yii::$app->taler->instances()->deleteInstance('shop-1', purge: true);

Authentication & Access Tokens

use Taler\Api\Instance\Dto\LoginTokenRequest;
use Taler\Api\Instance\Dto\GetAccessTokensRequest;
use Taler\Api\Dto\RelativeTime;

// Request a login token
$token = Yii::$app->taler->instances()->getAccessToken(
    'shop-1',
    new LoginTokenRequest(
        scope: 'order-full',
        duration: new RelativeTime(3_600_000_000),
        description: 'Backoffice session',
    )
);
echo $token->access_token . PHP_EOL;

// List issued tokens
$tokens = Yii::$app->taler->instances()->getAccessTokens(
    'shop-1',
    new GetAccessTokensRequest(limit: -20)
);
if ($tokens !== null) {
    foreach ($tokens->tokens as $t) {
        echo $t->serial . ' ' . $t->scope . PHP_EOL;
    }
}

// Revoke current token
Yii::$app->taler->instances()->deleteAccessToken('shop-1');

// Revoke token by serial
Yii::$app->taler->instances()->deleteAccessTokenBySerial('shop-1', 123);

Update Auth

use Taler\Api\Instance\Dto\InstanceAuthConfigToken;

$result = Yii::$app->taler->instances()->updateAuth(
    'shop-1',
    new InstanceAuthConfigToken(password: 'new-secret')
);

Forgot Password

use Taler\Api\Instance\Dto\InstanceAuthConfigToken;

$result = Yii::$app->taler->instances()->forgotPassword(
    'shop-1',
    new InstanceAuthConfigToken(password: 'new-password')
);

KYC Status

use Taler\Api\Instance\Dto\GetKycStatusRequest;

$kyc = Yii::$app->taler->instances()->getKycStatus('shop-1');

if ($kyc !== null) {
    foreach ($kyc->kyc_data as $entry) {
        echo $entry->exchange_url . PHP_EOL;
    }
}

Merchant Statistics

use Taler\Api\Instance\Dto\GetMerchantStatisticsAmountRequest;
use Taler\Api\Instance\Dto\GetMerchantStatisticsCounterRequest;

$amounts = Yii::$app->taler->instances()->getMerchantStatisticsAmount(
    'shop-1',
    'ORDERS',
    new GetMerchantStatisticsAmountRequest(by: 'BUCKET')
);

$counters = Yii::$app->taler->instances()->getMerchantStatisticsCounter(
    'shop-1',
    'VISITS',
    new GetMerchantStatisticsCounterRequest(by: 'INTERVAL')
);

Templates API

The Templates API is accessible via Yii::$app->taler->templates().

List Templates

$response = Yii::$app->taler->templates()->getTemplates();

foreach ($response->templates as $template) {
    echo $template->template_id . '' . $template->template_description . PHP_EOL;
}

Get Template Details

$details = Yii::$app->taler->templates()->getTemplate('coffee-small');

echo $details->template_description . PHP_EOL;
echo $details->template_contract->summary . PHP_EOL;
echo $details->template_contract->amount . PHP_EOL;
echo $details->template_contract->minimum_age . PHP_EOL;

Create Template

use Taler\Api\Dto\RelativeTime;
use Taler\Api\Templates\Dto\TemplateAddDetails;
use Taler\Api\Templates\Dto\TemplateContractDetails;

Yii::$app->taler->templates()->createTemplate(
    new TemplateAddDetails(
        template_id: 'coffee-small',
        template_description: 'Small coffee in paper cup',
        template_contract: new TemplateContractDetails(
            minimum_age: 0,
            pay_duration: new RelativeTime(900_000_000),
            summary: 'Small coffee',
            currency: 'KUDOS',
            amount: 'KUDOS:2.50'
        ),
        otp_id: null,
        editable_defaults: ['extra_note' => true]
    )
);

Update Template

use Taler\Api\Dto\RelativeTime;
use Taler\Api\Templates\Dto\TemplatePatchDetails;
use Taler\Api\Templates\Dto\TemplateContractDetails;

Yii::$app->taler->templates()->updateTemplate(
    'coffee-small',
    new TemplatePatchDetails(
        template_description: 'Small coffee (updated)',
        template_contract: new TemplateContractDetails(
            minimum_age: 0,
            pay_duration: new RelativeTime(1_200_000_000),
            summary: 'Small coffee (updated)',
            currency: 'KUDOS',
            amount: 'KUDOS:2.80'
        ),
        otp_id: null,
        editable_defaults: ['extra_note' => true]
    )
);

Delete Template

Yii::$app->taler->templates()->deleteTemplate('coffee-small');

Token Families API

The Token Families API is accessible via Yii::$app->taler->tokenFamilies().

List Token Families

$families = Yii::$app->taler->tokenFamilies()->getTokenFamilies();

foreach ($families->token_families as $family) {
    echo $family->slug . '' . $family->name . '' . $family->kind . PHP_EOL;
}

Get Token Family Details

$family = Yii::$app->taler->tokenFamilies()->getTokenFamily('loyalty-token');

echo $family->slug . PHP_EOL;
echo $family->name . PHP_EOL;
echo $family->description . PHP_EOL;
echo $family->kind . PHP_EOL;
echo 'issued=' . $family->issued . ', used=' . $family->used . PHP_EOL;

Create Token Family

use Taler\Api\Dto\RelativeTime;
use Taler\Api\Dto\Timestamp;
use Taler\Api\TokenFamilies\Dto\TokenFamilyCreateRequest;

Yii::$app->taler->tokenFamilies()->createTokenFamily(
    new TokenFamilyCreateRequest(
        slug: 'loyalty-token',
        name: 'Loyalty Program',
        description: 'Discount token family for recurring buyers',
        valid_before: new Timestamp(1750000000),
        duration: new RelativeTime(86400000000),
        validity_granularity: new RelativeTime(3600000000),
        start_offset: new RelativeTime(0),
        kind: 'discount',
        description_i18n: ['de' => 'Rabatt-Tokenfamilie'],
        extra_data: ['trusted_domains' => ['merchant.example']],
        valid_after: new Timestamp(1710000000)
    )
);

Update Token Family

use Taler\Api\Dto\Timestamp;
use Taler\Api\TokenFamilies\Dto\TokenFamilyUpdateRequest;

Yii::$app->taler->tokenFamilies()->updateTokenFamily(
    'loyalty-token',
    new TokenFamilyUpdateRequest(
        name: 'Loyalty Program (Updated)',
        description: 'Updated description for recurring buyers',
        valid_after: new Timestamp(1710000000),
        valid_before: new Timestamp(1755000000),
        description_i18n: ['de' => 'Aktualisierte Rabatt-Tokenfamilie'],
        extra_data: ['trusted_domains' => ['merchant.example', 'shop.example']]
    )
);

Delete Token Family

Yii::$app->taler->tokenFamilies()->deleteTokenFamily('loyalty-token');

Async Support

All API methods support asynchronous execution by appending Async to the method name. Async methods return a promise that resolves to the same typed DTO as the synchronous variant.

// Example: create an order asynchronously
$promise = Yii::$app->taler->orders()->createOrderAsync(
    new PostOrderRequest(order: $order)
);

$response = $promise->wait();
echo 'Created order (async): ' . $response->order_id . PHP_EOL;

// Example: list instances asynchronously
$promise = Yii::$app->taler->instances()->getInstancesAsync();
$list = $promise->wait();

Bank Accounts API

The Bank Accounts API is accessible via Yii::$app->taler->bankAccounts().

List Bank Accounts

$accounts = Yii::$app->taler->bankAccounts()->getAccounts();

foreach ($accounts->accounts as $entry) {
    echo $entry->h_wire . ' ' . $entry->payto_uri . PHP_EOL;
}

Get Bank Account Details

$account = Yii::$app->taler->bankAccounts()->getAccount('h_wire_hash_here');

echo $account->payto_uri . PHP_EOL;
echo $account->active ? 'active' : 'inactive';

Create Bank Account

use Taler\Api\BankAccounts\Dto\AccountAddDetails;
use Taler\Api\BankAccounts\Dto\AccountAddResponse;
use Taler\Api\TwoFactorAuth\Dto\ChallengeResponse;

$result = Yii::$app->taler->bankAccounts()->createAccount(
    new AccountAddDetails(
        payto_uri: 'payto://iban/DE75512108001245126199?receiver-name=Merchant'
    )
);

if ($result instanceof ChallengeResponse) {
    echo '2FA required: ' . $result->getChallengeId() . PHP_EOL;
} elseif ($result instanceof AccountAddResponse) {
    echo 'h_wire: ' . $result->h_wire . PHP_EOL;
}

Update Bank Account

use Taler\Api\BankAccounts\Dto\AccountPatchDetails;

Yii::$app->taler->bankAccounts()->updateAccount(
    'h_wire_hash_here',
    new AccountPatchDetails(
        credit_facade_url: 'https://bank-facade.example'
    )
);

Delete Bank Account

Yii::$app->taler->bankAccounts()->deleteAccount('h_wire_hash_here');

OTP Devices API

The OTP Devices API is accessible via Yii::$app->taler->otpDevices().

List OTP Devices

$response = Yii::$app->taler->otpDevices()->getOtpDevices();

foreach ($response->otp_devices as $device) {
    echo $device->otp_device_id . '' . $device->device_description . PHP_EOL;
}

Get OTP Device Details

use Taler\Api\OtpDevices\Dto\GetOtpDeviceRequest;

$details = Yii::$app->taler->otpDevices()->getOtpDevice(
    'pos-terminal-1',
    new GetOtpDeviceRequest(price: 'KUDOS:1.00')
);

echo $details->device_description . PHP_EOL;
echo $details->otp_algorithm . PHP_EOL;
echo $details->otp_timestamp . PHP_EOL;
echo ($details->otp_code ?? '(none)') . PHP_EOL;

Create OTP Device

use Taler\Api\OtpDevices\Dto\OtpDeviceAddDetails;

Yii::$app->taler->otpDevices()->createOtpDevice(
    new OtpDeviceAddDetails(
        otp_device_id: 'pos-terminal-1',
        otp_device_description: 'Front desk terminal',
        otp_key: 'JBSWY3DPEHPK3PXP',
        otp_algorithm: 'TOTP_WITHOUT_PRICE',
    )
);

Update OTP Device

use Taler\Api\OtpDevices\Dto\OtpDevicePatchDetails;

Yii::$app->taler->otpDevices()->updateOtpDevice(
    'pos-terminal-1',
    new OtpDevicePatchDetails(
        otp_device_description: 'Front desk terminal (updated)',
        otp_algorithm: 'TOTP_WITH_PRICE',
    )
);

Delete OTP Device

Yii::$app->taler->otpDevices()->deleteOtpDevice('pos-terminal-1');

Two-Factor Auth API

The Two-Factor Auth API is accessible via Yii::$app->taler->twoFactorAuth().

Request Challenge Transmission

$response = Yii::$app->taler->twoFactorAuth()->requestChallenge(
    'sandbox',
    'challenge-id-123',
    ['resend' => true]
);

echo 'solve_expiration: ' . $response->solve_expiration->t_s . PHP_EOL;
echo 'earliest_retransmission: ' . $response->earliest_retransmission->t_s . PHP_EOL;

Confirm Challenge

use Taler\Api\TwoFactorAuth\Dto\MerchantChallengeSolveRequest;

Yii::$app->taler->twoFactorAuth()->confirmChallenge(
    'sandbox',
    'challenge-id-123',
    new MerchantChallengeSolveRequest(tan: '123456')
);

Async Two-Factor Calls

use Taler\Api\TwoFactorAuth\Dto\MerchantChallengeSolveRequest;

$requestPromise = Yii::$app->taler->twoFactorAuth()->requestChallengeAsync(
    'sandbox',
    'challenge-id-123',
    []
);
$challenge = $requestPromise->wait();

$confirmPromise = Yii::$app->taler->twoFactorAuth()->confirmChallengeAsync(
    'sandbox',
    'challenge-id-123',
    new MerchantChallengeSolveRequest(tan: '123456')
);
$confirmPromise->wait();

Donau Charity API

The Donau Charity API is accessible via Yii::$app->taler->donauCharities().

List Linked Donau Charity Instances

$response = Yii::$app->taler->donauCharities()->getInstances();

foreach ($response->donau_instances as $instance) {
    echo '#' . $instance->donau_instance_serial . ' ' . $instance->charity_name . PHP_EOL;
}

Link a Donau Charity

use Taler\Api\DonauCharity\Dto\PostDonauRequest;
use Taler\Api\TwoFactorAuth\Dto\ChallengeResponse;

$result = Yii::$app->taler->donauCharities()->createDonauCharity(
    new PostDonauRequest(
        donau_url: 'https://donau.example',
        charity_id: 42,
    )
);

if ($result instanceof ChallengeResponse) {
    echo '2FA required: ' . $result->getChallengeId() . PHP_EOL;
}

Unlink a Donau Charity by Serial

Yii::$app->taler->donauCharities()->deleteDonauCharityBySerial(42);

Testing

vendor/bin/phpunit

Funding

This project is funded through NGI TALER Fund, a fund established by NLnet with financial support from the European Commission's Next Generation Internet program. Learn more at the NLnet project page.

NLnet foundation logo