Skip to main content
When a buyer purchases one of your app’s one-time items, or that purchase is later refunded, charged back, or cancelled, Fanvue sends a webhook to your app so you can fulfil and reconcile the order. There are exactly two events:
Event (type)Fires when
app.payment.succeededA one-time (appotp_) purchase of your app is paid
app.payment.refundedA one-time (appotp_) purchase is refunded, charged back, or cancelled
These webhooks cover one-time purchases only, identified by a purchase_reference with the appotp_ prefix. App subscriptions do not emit webhooks; read subscription state with the App Subscriptions endpoints instead.

Delivery model

Unlike the creator checkout webhooks, you do not subscribe to these events. Fanvue delivers them automatically to your app’s own webhook tenant as soon as your app has a paid one-time item, so there is no connector API call to set up a subscription. This is a different delivery channel from the creator-tenant webhooks: the checkout webhooks and platform event webhooks are delivered to the creator’s tenant, whereas app.payment.* events are delivered to the purchased app’s tenant.
There is no app.payment.failed event. A failed one-time purchase is terminal, the buyer simply retries, so nothing is delivered for a failure. Likewise there is no app.payment.pending.

Event envelope

Every app payment webhook is delivered as an HTTP POST with the same Standard-Webhooks-style envelope used by checkout webhooks:
{
  "id": "<event id>",
  "type": "app.payment.succeeded",
  "timestamp": "2026-06-17T13:12:45.123Z",
  "data": { "object": "payment", "...": "resource fields" }
}
  • id: unique event id, stable across delivery retries (use it to dedupe).
  • type: app.payment.succeeded or app.payment.refunded.
  • timestamp: ISO 8601 time the event was emitted.
  • data: the resource object. data.object is "payment" for a succeeded purchase and "refund" for a refund.
Monetary amounts are integers in the currency’s minor units (for USD, cents, so 999 means $9.99).

app.payment.succeeded

data.object is "payment".
FieldTypeDescription
objectstringAlways "payment"
idstringFanvue invoice number for the payment
statusstringAlways "succeeded"
grossintegerGross amount in minor units
currencystringISO 4217 currency code
purchase_referencestringThe one-time purchase reference (appotp_ prefix)
client_reference_idstring | nullYour passthrough reference from the buyer flow
created_atstringISO 8601 creation time
paid_atstringISO 8601 time the payment was paid
itemobject | null{ uuid } of the purchased pricing plan (uuid may be null)
appobject{ uuid } of your app
buyerobject{ uuid } of the buyer
metadataobject | nullPassthrough metadata echoed from the buyer flow (may be null or empty)
{
  "id": "9f2c1e7a4b8d6f30a1c2e3d4b5a6978c0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b",
  "type": "app.payment.succeeded",
  "timestamp": "2026-06-17T13:12:45.123Z",
  "data": {
    "object": "payment",
    "id": "INV-2026-000123",
    "status": "succeeded",
    "gross": 999,
    "currency": "USD",
    "purchase_reference": "appotp_3f9a2b71-1c4e-4f8a-9d2b-7c6e5a4b3d21",
    "client_reference_id": "your-app-order-42",
    "created_at": "2026-06-17T13:12:40.000Z",
    "paid_at": "2026-06-17T13:12:44.880Z",
    "item": { "uuid": "b2d7c9f0-4a13-4e6b-8f25-1a9c3e7d5b80" },
    "app": { "uuid": "a1c3e5f7-9b2d-4c6e-8a0f-2d4b6c8e0a13" },
    "buyer": { "uuid": "c4e6a8b0-2d4f-6a81-0c2e-4b6d8f0a2c46" }
  }
}

app.payment.refunded

data.object is "refund". The refund resource references the original payment via payment_id, carries amount (not gross), and has no status or paid_at.
FieldTypeDescription
objectstringAlways "refund"
idstringFanvue invoice number for the refund
payment_idstringInvoice number of the original app.payment.succeeded payment
amountintegerRefunded amount in minor units
currencystringISO 4217 currency code
reasonstringrefund | chargeback | cancel
purchase_referencestringThe one-time purchase reference (appotp_ prefix)
client_reference_idstring | nullYour passthrough reference from the buyer flow
created_atstringISO 8601 creation time
itemobject | null{ uuid } of the purchased pricing plan (uuid may be null)
appobject{ uuid } of your app
buyerobject{ uuid } of the buyer
metadataobject | nullPassthrough metadata echoed from the buyer flow (may be null or empty)
{
  "id": "1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b",
  "type": "app.payment.refunded",
  "timestamp": "2026-06-18T09:30:00.000Z",
  "data": {
    "object": "refund",
    "id": "INV-2026-000456",
    "payment_id": "INV-2026-000123",
    "amount": 999,
    "currency": "USD",
    "reason": "refund",
    "purchase_reference": "appotp_3f9a2b71-1c4e-4f8a-9d2b-7c6e5a4b3d21",
    "client_reference_id": "your-app-order-42",
    "created_at": "2026-06-18T09:29:58.000Z",
    "item": { "uuid": "b2d7c9f0-4a13-4e6b-8f25-1a9c3e7d5b80" },
    "app": { "uuid": "a1c3e5f7-9b2d-4c6e-8a0f-2d4b6c8e0a13" },
    "buyer": { "uuid": "c4e6a8b0-2d4f-6a81-0c2e-4b6d8f0a2c46" }
  }
}
reason tells the three refund paths apart: refund (a normal refund), chargeback (a dispute resolved against the sale), and cancel (the purchase was cancelled). All three arrive as app.payment.refunded.

Verifying signatures

App payment webhooks are signed exactly like the rest of Fanvue’s webhooks, with an X-Fanvue-Signature header (t=<timestamp>,v0=<hmac-sha256-hex>). Verify it against the raw request body before parsing JSON. The header breakdown, the verification flow, and complete Node and Python samples live in Verify Webhook Signatures.

Reconciliation

Webhooks can be missed or delayed, so reconcile against the app one-time payments REST endpoints. They are read-only, require the read:self scope, and split into owner-scoped (all buyers of an app you own) and caller-scoped (/me, the authenticated user’s own payments):
EndpointScopeReturns
GET /apps/{appUuid}/paymentsOwnerAll buyers’ one-time payments for an app you own
GET /apps/{appUuid}/payments/{invoiceNumber}OwnerOne payment by Fanvue invoice number
GET /apps/{appUuid}/payments/meCallerThe authenticated user’s own one-time payments for the app
GET /apps/{appUuid}/payments/me/{invoiceNumber}CallerThe caller’s own payment by invoice number
The invoiceNumber path parameter matches data.id (for a payment) or data.payment_id (for the original payment behind a refund).
These payment endpoints are not yet part of the generated API Reference; they are documented here until they appear in the OpenAPI spec.