---
title: "billing.vibes.charge.paid@1 · OpenVibe.Services"
canonical: "https://openvibe.services/docs/contracts/billing.vibes.charge.paid"
updated: null
---

# billing.vibes.charge.paid@1 · OpenVibe.Services

HTML: [billing.vibes.charge.paid@1 · OpenVibe.Services](https://openvibe.services/docs/contracts/billing.vibes.charge.paid)

# BillingVibesChargePaidPayload `billing.vibes.charge.paid@1`

Generated at 2026-11-13 06:25:48 UTC from [`openvibe-contracts v0.419.0`](https://github.com/OpenVibers/OpenVibe.Contracts/tree/v0.419.0) and [`openvibe-sdk v0.161.0`](https://github.com/OpenVibers/OpenVibe.SDK/tree/v0.161.0).

- Version: 1.0.0
- Owner: billing
- Visibility: public
- Status: active
- Compatibility: backward
- Schema: [`https://openvibe.network/contracts/events/payloads/billing.vibes.charge.paid.v1.json`](/docs/contracts/billing.vibes.charge.paid.json)

billing.vibes.charge.paid v1 (OpenVibe.Billing, written when a Vibes Pay charge is paid): the app's charge and the opaque payer reference. This is also the Vibes Pay webhook body (HMAC-SHA256 with the app's webhook secret, retried with backoff). It deliberately carries no person id and no email, only payer\_ref, stable per app, so an app can recognise a returning payer without receiving their identity. amount is whole Vibes. Envelope: subject { type: vibes-charge, id: vch\_... }, actor service:billing.

## Fields

| Field | Type | Required | Description | Constraints |
| --- | --- | --- | --- | --- |
| `id` | string | yes | The charge; GET /api/v1/vibes/charges/:id returns its current state. | - `pattern ^vch_[0-9A-HJKMNP-TV-Z]{26}$` |
| `app` | string | yes | The app that created the charge. | - `pattern ^app_[0-9A-HJKMNP-TV-Z]{26}$` |
| `project_id` | string | yes | The app's project, whose Vibes wallet received the charge. | - `pattern ^prj_[0-9A-HJKMNP-TV-Z]{26}$` |
| `amount` | integer | yes | The whole number of Vibes paid. | - `minimum 1` |
| `description` | string | yes |  | - `minLength 1`<br>- `maxLength 140` |
| `reference` | string | yes | The app's reference, as given when the charge was created. | - `minLength 1`<br>- `maxLength 128` |
| `status` | const | yes |  | - `= "paid"` |
| `created_at` | string | yes |  | - `format date-time` |
| `paid_at` | string | yes | When the Vibes moved from the person to the project. | - `format date-time` |
| `refunded_at` | null | yes | Null: a paid charge has not been refunded. |  |
| `expires_at` | string | yes |  | - `format date-time` |
| `sandbox` | boolean | yes | True for a sandbox app: sandbox Vibes only, worth nothing. |  |
| `payer_ref` | string | yes | Opaque reference for the paying person, stable per app. Never the person's id, email or any other identity; an app uses it to recognise a returning payer. | - `pattern ^[A-Za-z0-9._:-]+$`<br>- `minLength 8`<br>- `maxLength 128` |

## Examples

From the contract's own test fixtures: valid ones validate, rejected ones must fail.

 Valid: paid

~~~~
{
  "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "amount": 50,
  "description": "Sticker pack: Neon cats",
  "reference": "order_123",
  "status": "paid",
  "created_at": "2026-11-04T12:00:00.000Z",
  "paid_at": "2026-11-04T12:01:00.000Z",
  "refunded_at": null,
  "expires_at": "2026-11-04T12:30:00.000Z",
  "sandbox": false,
  "payer_ref": "payer_7f3c9a12"
}
~~~~

Valid: sandbox

~~~~
{
  "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "amount": 50,
  "description": "Sticker pack: Neon cats",
  "reference": "order_123",
  "status": "paid",
  "created_at": "2026-11-04T12:00:00.000Z",
  "paid_at": "2026-11-04T12:01:00.000Z",
  "refunded_at": null,
  "expires_at": "2026-11-04T12:30:00.000Z",
  "sandbox": true,
  "payer_ref": "payer_7f3c9a12"
}
~~~~

Rejected: float-amount

~~~~
{
  "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "amount": 50.5,
  "description": "Sticker pack: Neon cats",
  "reference": "order_123",
  "status": "paid",
  "created_at": "2026-11-04T12:00:00.000Z",
  "paid_at": "2026-11-04T12:01:00.000Z",
  "refunded_at": null,
  "expires_at": "2026-11-04T12:30:00.000Z",
  "sandbox": false,
  "payer_ref": "payer_7f3c9a12"
}
~~~~

Rejected: payer-ref-email

~~~~
{
  "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "amount": 50,
  "description": "Sticker pack: Neon cats",
  "reference": "order_123",
  "status": "paid",
  "created_at": "2026-11-04T12:00:00.000Z",
  "paid_at": "2026-11-04T12:01:00.000Z",
  "refunded_at": null,
  "expires_at": "2026-11-04T12:30:00.000Z",
  "sandbox": false,
  "payer_ref": "alice@example.com"
}
~~~~

Rejected: person-id

~~~~
{
  "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "amount": 50,
  "description": "Sticker pack: Neon cats",
  "reference": "order_123",
  "status": "paid",
  "created_at": "2026-11-04T12:00:00.000Z",
  "paid_at": "2026-11-04T12:01:00.000Z",
  "refunded_at": null,
  "expires_at": "2026-11-04T12:30:00.000Z",
  "sandbox": false,
  "payer_ref": "payer_7f3c9a12",
  "user_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
}
~~~~

## Validate

~~~~
const contracts = require('openvibe-contracts');
contracts.validate('billing.vibes.charge.paid@1', value);   // { valid, errors: [{ path, message }] }
~~~~
