> ## Documentation Index
> Fetch the complete documentation index at: https://docs.profy.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# Billing Formulas

> The complete algorithm for all five billing units, credit deduction order, and revenue share formulas

# Billing Formulas

This page documents **formulas**, not rates.

The reason is direct: rates are operational data that shift as models come and go and suppliers reprice — anything written here could be stale tomorrow. Formulas are code, and changing them requires a release. What you need is to know how the number was produced, so you can check a bill yourself.

<Info>
  Everything settles in **credits**. The exchange rate is fixed: **1 CNY = 100 credits** (code constant `CREDITS_PER_YUAN = 100`).
</Info>

## The common pipeline

Regardless of billing unit, every charge runs the same chain:

<Steps>
  <Step title="Resolve the billing unit">
    The model config declares which unit it bills on: `token_split` / `per_call` / `per_ten_thousand_characters` / `duration_second` / `cost_matrix`.
  </Step>

  <Step title="Compute supplier cost, or rate directly">
    With a cost matrix, parameter conditions are matched first to get a cost in CNY. Without one, the configured rate is applied directly.
  </Step>

  <Step title="Apply margin">
    `sellPrice(CNY) = cost(CNY) × (1 + marginPercent / 100)`
  </Step>

  <Step title="Convert to credits, rounding up">
    `credits = max(1, ceil(sellPrice × 100))`
  </Step>

  <Step title="Apply parameter coefficient">
    When a parameter (resolution, mode, etc.) carries a coefficient: `finalCredits = ceil(baseCredits × coefficient)`
  </Step>
</Steps>

<Warning>
  Every step uses `ceil`, never rounding, and the **minimum charge is 1 credit** (`max(1, ...)`). A tiny call is therefore never free. Many small calls cost slightly more in aggregate than one call of the same total volume — rounding happens on each one, not at the end.
</Warning>

## 1. `token_split`: input and output priced separately

Used by conversational models. Input and output have separate rates in **credits per million tokens**.

```
inputCharge = ceil(inputTokens × inputRate / 1,000,000)
outputCharge = ceil(outputTokens × outputRate / 1,000,000)
baseCharge  = inputCharge + outputCharge
finalCharge = max(1, hasFactors ? ceil(baseCharge × coefficient) : baseCharge)
```

<Warning>
  Note that `ceil` runs **once on input and once on output** before they are summed. So a single call costs at least 2 credits (1 each) even with a trivial token count.
</Warning>

### Fallback when tokens are not itemized

Some suppliers return only a total token count. In that case a blended rate applies:

```
blendedRate = tokenRate > 0
                ? tokenRate
                : ceil((inputRate + outputRate) / 2)

baseCharge  = ceil(totalTokens × blendedRate / 1,000,000)
```

The blended rate is the **arithmetic mean of input and output rates**. Real conversations produce far fewer output tokens than input, so the fallback path is usually more expensive than the itemized path. If a model's bills look higher than expected, first check whether it reports itemized tokens at all.

### Supplier cost recording

When supplier costs are configured, they are recorded alongside (this does not affect your charge; it is for platform reconciliation):

```
supplierCost(CNY) = (inputTokens × costInputYuan + outputTokens × costOutputYuan) / 1,000,000
```

## 2. `duration_second`: priced per second

Used by video generation, sandbox runtime, and similar.

```
baseCharge  = ceil(seconds × durationRate)
finalCharge = max(1, hasFactors ? ceil(baseCharge × coefficient) : baseCharge)
```

Under a cost matrix:

```
cost(CNY)      = mode === "per_unit" ? unitCostYuan × seconds : unitCostYuan   // flat = fixed
sellPrice(CNY) = cost × (1 + margin / 100)
credits        = max(1, ceil(sellPrice × 100))
```

`flat` exists because some suppliers charge per generation rather than per second — same duration unit, but selecting `flat` in the rule collapses it to a fixed price.

## 3. `per_call`: priced per call

Used by image generation and other one-call-one-result capabilities.

```
baseCharge  = ceil(callCount × perCallRate)
finalCharge = max(1, hasFactors ? ceil(baseCharge × coefficient) : baseCharge)
```

Batch generation (`generate_batch`) counts actual images with no volume discount.

## 4. `per_ten_thousand_characters`: priced per 10K characters

Used by TTS, translation, and other text-volume capabilities. It shares an implementation with `per_call` and adds only a divisor:

```
billableQuantity = characterCount / 10,000
baseCharge       = ceil(billableQuantity × rate)
finalCharge      = max(1, hasFactors ? ceil(baseCharge × coefficient) : baseCharge)
```

<Note>
  Cost-rule matching uses the **raw character count** (`callCount`); the division by 10,000 applies only when computing the amount. So a tiered rule like "over 50,000 characters switches rate" must be written against raw characters, not per-10K units.
</Note>

## 5. `cost_matrix`: parameter conditions → cost

Not a fifth billing unit but a **pricing strategy layered on top of the other four**. It lets one model price differently by parameter (1024x1024 versus 4K, for example).

Rule structure:

```json theme={null}
{
  "rules": [
    {
      "conditions": [
        { "key": "resolution", "values": ["4k", "2160p"] }
      ],
      "unitCostYuan": 0.5,
      "mode": "per_unit"
    },
    {
      "conditions": [],
      "unitCostYuan": 0.1,
      "mode": "per_unit"
    }
  ]
}
```

Matching rules:

* Rules are **evaluated in order; the first rule whose conditions all match wins**
* Multiple conditions on one rule are **AND**-combined
* A rule with an empty `conditions` array **matches everything** and serves as the fallback
* Conditions come in three shapes: enum (`values`, any-of), range (`min` / `max`, with `minExclusive` / `maxExclusive` choosing open or closed), and exact match (`equals`)

<Warning>
  The fallback rule must be **last**. Placed earlier it matches first and every refined rule below becomes unreachable — with no error, just everyone billed at the fallback price.
</Warning>

Token units and per\_call / duration units use different cost fields in a rule:

| Unit                 | Cost fields                                                 |
| -------------------- | ----------------------------------------------------------- |
| token                | `inputCostYuan` + `outputCostYuan` (CNY per million tokens) |
| per\_call / duration | `unitCostYuan` + `mode` (`per_unit` scaled / `flat` fixed)  |

**Sell price is never stored**: only cost plus margin, computed at billing time. Adjusting margin means changing one number, with no historical backfill.

## Credit deduction order (six buckets)

Your balance is not one number but **six buckets**, consumed in strict order:

<Steps>
  <Step title="1. Daily check-in">
    Expires at midnight (UTC+8) the next day. Spent first because it lapses soonest.
  </Step>

  <Step title="2. Signup grant">
    Valid 90 days.
  </Step>

  <Step title="3. Plan grant">
    Valid for one subscription cycle.
  </Step>

  <Step title="4. Referral reward">
    Reclaimed after 100 days of inactivity.
  </Step>

  <Step title="5. Top-up bonus">
    Permanent.
  </Step>

  <Step title="6. Top-up principal">
    Permanent, and **touched last**.
  </Step>
</Steps>

In one sentence: **spend what expires soonest first, and what you actually paid for last**. Within a bucket, lots are consumed by expiry order.

<Info>
  Expiry reclamation targets a specific lot rather than following the priority order — the reclamation record carries the source transaction ID and deducts exactly that lot's remainder. Only when no matching lot is found does it fall back to priority order. This guarantees that "signup credits expired" never eats into your paid balance.
</Info>

Unknown or legacy grant types are filed into the top-up principal bucket (permanent, lowest priority) — the conservative choice, preferring to deduct late over silently swallowing user balance.

## Expert revenue share

Creator earnings follow two independent paths.

### Buyout (unlocking an expert)

```
creatorEarnings(diamonds) = unlockPrice × buyoutSharePpm / 1,000,000
```

Shares by creator level (ppm, parts per million):

| Level          | Threshold (cumulative diamonds) | `buyoutSharePpm` | Effective |
| -------------- | ------------------------------- | ---------------- | --------- |
| `rising`       | 0                               | 750,000          | 75%       |
| `growing`      | 300,000                         | 850,000          | 85%       |
| `professional` | 1,000,000                       | 900,000          | 90%       |
| `master`       | 5,000,000                       | 950,000          | 95%       |

### Usage share (consumption)

```
profit                    = userCreditsSpent − model and tool cost
creatorEarnings(diamonds) = profit × consumptionSharePpm / 1,000,000
```

| Level          | `consumptionSharePpm` | Effective |
| -------------- | --------------------- | --------- |
| `rising`       | 100,000               | 10%       |
| `growing`      | 150,000               | 15%       |
| `professional` | 200,000               | 20%       |
| `master`       | 200,000               | 20%       |

<Warning>
  Two points are routinely misread:

  1. **Usage share is computed from profit, not gross spend.** If a user spends 1,000 credits of which 700 is model cost, a `professional` creator earns `300 × 20% = 60` diamonds, not `1,000 × 20% = 200`.
  2. **Self-purchases do not earn.** Buying your own expert from your own account produces no share, which blocks wash trading.
</Warning>

The usage share stops rising after `professional` (20%) — reaching `master` only raises the buyout share.

## Withdrawal conversion

```
withdrawableAmount(CNY) = unfrozenDiamonds / exchangeRate      // default exchangeRate = 100
```

That is **100 diamonds = 1 CNY**. Freeze periods and thresholds are in [Limits](/en/documentation/reference/limits).

<Warning>
  Withdrawal is currently hard-disabled in code; every request returns `WITHDRAWAL_NOT_OPEN`. The formula above applies once it opens.
</Warning>

## Checking a bill yourself

<Steps>
  <Step title="Get the unit and rate for the call">
    The consumption record carries `pricingUnit` and a rate snapshot.
  </Step>

  <Step title="Compute by hand using this page">
    Remember every step uses `ceil` with a 1-credit floor.
  </Step>

  <Step title="If it doesn't match, check three things">
    * Whether a parameter coefficient applied (`factorCoefficient` other than 1)
    * Whether the cost matrix was used instead of a flat rate
    * Whether a token-unit call fell back to the blended rate because the supplier returned no itemization
  </Step>
</Steps>

Those three account for nearly every "the math doesn't add up," and all three are visible as fields on the consumption record.

## Related pages

<CardGroup cols={2}>
  <Card title="Limits" icon="gauge" href="/en/documentation/reference/limits">
    Quotas, validity, rate limits
  </Card>

  <Card title="Credits and consumption" icon="coins" href="/en/documentation/billing/credits">
    Earning and checking credits
  </Card>

  <Card title="Revenue system" icon="chart-line" href="/en/creators/revenue-system">
    Creator earnings in full
  </Card>

  <Card title="Pricing and billing" icon="tag" href="/en/creators/pricing-and-billing">
    Pricing your own expert
  </Card>
</CardGroup>

<Note>
  Verified 2026-08-11. Sources: `services/core/src/db/service/credit-consume.ts` (billing pipeline and six-bucket priority), `services/core/src/constants/cost-matrix.ts` (cost rules), `services/core/src/db/service/platform-config.ts` (revenue shares and withdrawal rules). Rate and cost values are ops data and are deliberately omitted.
</Note>
