---
title: Shared Ecosystem Wallet & Balance Synchronization
category: Shared Wallet & Ledger
order: 10
badge: Shared Wallet
description: Architectural specification for the centralized ledger balance synced across ternisdomains.de, thosted.de, and Ternis Auth applications.
---

# Shared Ecosystem Wallet & Balance Synchronization

Ternis provides a **Unified Platform Credit Ledger** that allows users to deposit funds once in their centralized Ternis Account and seamlessly spend those credits across ecosystem applications such as **`ternisdomains.de`** (domain registrations & renewals), **`thosted.de`** (cloud servers), and developer API calls.

---

## 1. Architectural Design & ACID Ledger

All financial movements are recorded as immutable entries in the `balance_transactions` table:

```
+-------------------------------------------------------------+
|                     User Account Balance                    |
|                users.balance_cents (e.g. 5000)              |
+-------------------------------------------------------------+
                              |
       +----------------------+----------------------+
       |                      |                      |
       v                      v                      v
[ Top-Up / Stripe ]   [ ternisdomains.de ]     [ thosted.de ]
 +€25.00 Deposit       -€12.50 Domain Reg       -€5.00 Compute
```

### Key Guarantees:
1. **Strict Idempotency**: Each charge request requires or accepts an `idempotency_key`. If network timeouts cause `ternisdomains.de` to retry a purchase request, the platform returns the previous transaction rather than charging the user twice.
2. **Pessimistic Row Locking (`SELECT ... FOR UPDATE`)**: Deductions and top-ups execute inside ACID transactions with row-level locks, eliminating race conditions when multiple microservices deduct balance simultaneously.
3. **Audit Trail**: Every transaction records `source_service`, `reference_id` (e.g. domain name `acme.de`), `balance_after_cents`, and metadata.

---

## 2. API Endpoints for Ecosystem Applications

### 2.1. Inspect Wallet Balance
Before showing checkout screens, applications can retrieve the current available balance:

```http
GET /api/v1/user/balance HTTP/1.1
Host: auth.ternis.net
Authorization: Bearer <access_token>
```

#### Response (200 OK):
```json
{
  "user_id": "b2f6b897-4001-460d-8386-db93f1d8c1c4",
  "balance_cents": 5000,
  "formatted_balance": "€50.00",
  "currency": "EUR",
  "recent_transactions": [
    {
      "id": "e4f81a20-3b4c-4d5e-9f0a-1b2c3d4e5f6a",
      "amount_cents": -1250,
      "formatted_amount": "-€12.50",
      "balance_after_cents": 5000,
      "formatted_balance_after": "€50.00",
      "type": "charge",
      "source_service": "ternisdomains.de",
      "reference_id": "domain:mycompany.de",
      "description": "Domain Registration: mycompany.de (1 year) via ternisdomains.de",
      "created_at": "2026-09-17T12:00:00Z"
    }
  ]
}
```

---

### 2.2. Charge Balance (e.g. `ternisdomains.de`)
When a user confirms a domain purchase:

```http
POST /api/v1/user/balance/charge HTTP/1.1
Host: auth.ternis.net
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "amount_cents": 1250,
  "service": "ternisdomains.de",
  "reference_id": "dom_reg_mycompany_de",
  "idempotency_key": "tdom_charge_20260917_00192",
  "description": "Domain registration: mycompany.de (1 year) via ternisdomains.de",
  "metadata": {
    "domain": "mycompany.de",
    "tld": "de",
    "years": 1
  }
}
```

#### Success Response (200 OK):
```json
{
  "success": true,
  "message": "Balance charged successfully.",
  "transaction": {
    "id": "9dc721a0-128f-4318-912b-31ca78198f12",
    "amount_cents": -1250,
    "formatted_amount": "-€12.50",
    "balance_after_cents": 3750,
    "formatted_balance_after": "€37.50",
    "currency": "EUR",
    "reference_id": "dom_reg_mycompany_de",
    "source_service": "ternisdomains.de"
  }
}
```

#### Insufficient Funds Response (422 Unprocessable Entity):
```json
{
  "success": false,
  "message": "Insufficient balance. Required: 1250 cents, Available: 500 cents.",
  "current_balance_cents": 500
}
```

---

## 3. Real-World Integration Blueprint for `ternisdomains.de`

```php
namespace App\Services;

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;

class TernisWalletClient
{
    protected string $authBaseUrl = 'https://auth.ternis.net/api/v1';

    public function purchaseDomain(string $userAccessToken, string $domainName, int $priceCents): array
    {
        $idempotencyKey = 'dom_order_' . md5($domainName . '_' . date('Y-m-d'));

        $response = Http::withToken($userAccessToken)
            ->post("{$this->authBaseUrl}/user/balance/charge", [
                'amount_cents' => $priceCents,
                'service' => 'ternisdomains.de',
                'reference_id' => $domainName,
                'idempotency_key' => $idempotencyKey,
                'description' => "Domain registration: {$domainName} via ternisdomains.de",
                'metadata' => [
                    'domain' => $domainName,
                    'timestamp' => now()->toIso8601String(),
                ],
            ]);

        if ($response->status() === 422) {
            throw new InsufficientWalletBalanceException('Please top up your Ternis Wallet balance.');
        }

        return $response->json();
    }
}
```
