charge.paid
Announces that a payment gateway charge was approved: a first purchase, a subscription renewal, a Publica One redemption or any later charge of an order. The payload below goes in the data field of the envelope, and follows the payload conventions.
When it is sent
Once per approved charge. A charge is what the gateway charged in one transaction; Publica.la records it as one payment per product of the order, plus a shipping payment when the order ships, all sharing gateway_transaction_id.
It is sent for every approved charge, with no filter:
- The first purchase of an order, every subscription renewal, and charges a 100% discount coupon covers.
- Publica One redemptions.
- A shipping charge alone.
- A charge approved on an order that is already cancelled, expired, refunded or otherwise closed.
Rejected, pending and refunded payments do not send it. A gateway notification Publica.la already recorded sends nothing again.
The idempotency key is charge.paid:order:<order id>:charge:<gateway transaction id>. It includes the order because a gateway can reuse a transaction id across orders.
Relation to order.paid
The first purchase of an order sends both order.paid and charge.paid, with the same payments. They are independent deliveries and can arrive in any order. Subscribe to order.paid to hear about each order once; subscribe to charge.paid to hear about every charge, renewals included.
Tell charges apart
Every charge sends the same event. Use these fields to tell what kind of charge it is:
| Charge | How to recognize it |
|---|---|
| Publica One redemption | A payment has sale_type publica_one_redemption. |
| Shipping charge alone | Every payment has sale_type shipping. |
| First charge of a subscription | charge.recurring_cycle is 1. |
| Subscription renewal | charge.recurring_cycle is greater than 1. |
| One-time purchase | charge.recurring_cycle is null, and the charge is none of the cases above. |
A later charge on an order that does not bill again, which is rare, also has recurring_cycle null. To tell it from the purchase, keep the ids of the orders you already received, or use order.paid, which is sent only for the first purchase.
data.order.status tells whether the order was closed when the charge arrived.
Payload
{
"charge": {
"gateway_type": "stripe",
"gateway_transaction_id": "pi_example_0001",
"status": "approved",
"recurring_cycle": null,
"amount_in_cents": 2248,
"tax_in_cents": 390,
"currency_id": "USD",
"paid_at": "2026-01-15T14:30:00Z"
},
"order": {
"id": "1001",
"uuid": "9b2f6c1e-4a7d-4e8b-9f3a-2c5d8e1f0a42",
"external_id": "ORD-1001",
"status": "approved",
"type": "internal",
"recurring": false,
"currency_id": "USD",
"amount_in_cents": 2248,
"tax_in_cents": 390,
"gateway_type": "stripe",
"payment_method": "card",
"created_at": "2026-01-15T14:30:00Z"
},
"user": {
"id": "501",
"uuid": "3e7a1d2c-8b4f-4c6e-a9d1-7f2b5c8e0d13",
"external_id": "USR-501",
"name": "Jane Buyer",
"billing_information": {
"national_id": "20123456789",
"national_id_type": "CUIT",
"address_line_1": "Av. Example 123",
"address_line_2": null,
"city": "Buenos Aires",
"state": "CABA",
"country": "AR",
"postal_code": "C1001",
"other_information": null
}
},
"products": [
{
"id": "2001",
"type": "content",
"amount_in_cents": 1349,
"currency_id": "USD",
"content": {
"id": "301",
"external_id": "9780000000001",
"name": "An Example Book",
"file_type": "epub"
},
"plan": null,
"coupon": {
"code": "WELCOME10",
"discount_percentage": 10,
"shipping_discount_percentage": null,
"take_quantity": null,
"pay_quantity": null
}
},
{
"id": "2002",
"type": "content",
"amount_in_cents": 899,
"currency_id": "USD",
"content": {
"id": "302",
"external_id": "9780000000002",
"name": "Another Example Book",
"file_type": "pdf"
},
"plan": null,
"coupon": {
"code": "WELCOME10",
"discount_percentage": 10,
"shipping_discount_percentage": null,
"take_quantity": null,
"pay_quantity": null
}
}
],
"payments": [
{
"id": "4001",
"product_id": "2001",
"gateway_transaction_id": "pi_example_0001",
"status": "approved",
"sale_type": "retail",
"recurring_cycle": null,
"amount_in_cents": 1349,
"tax_in_cents": 234,
"tax_rate": 21,
"sale_country": "AR",
"sale_state": "CABA",
"currency_id": "USD",
"exchange_rate_to_usd": 1,
"tenant_payout_in_cents": 1003,
"payout_currency_id": "USD",
"paid_at": "2026-01-15T14:30:00Z",
"created_at": "2026-01-15T14:30:05Z"
},
{
"id": "4002",
"product_id": "2002",
"gateway_transaction_id": "pi_example_0001",
"status": "approved",
"sale_type": "retail",
"recurring_cycle": null,
"amount_in_cents": 899,
"tax_in_cents": 156,
"tax_rate": 21,
"sale_country": "AR",
"sale_state": "CABA",
"currency_id": "USD",
"exchange_rate_to_usd": 1,
"tenant_payout_in_cents": 669,
"payout_currency_id": "USD",
"paid_at": "2026-01-15T14:30:00Z",
"created_at": "2026-01-15T14:30:05Z"
}
]
}
Every amount_in_cents includes tax, with the tax it carries in tax_in_cents. The charge's is what the gateway charged, and equals the sum of its payments' amount_in_cents. In the example, 1349 + 899 = 2248.
data.charge
| Field | Type | Description |
|---|---|---|
gateway_type | string | Payment gateway that processed the charge, for example stripe. |
gateway_transaction_id | string or null | Transaction id at the payment gateway. Every payment of the charge has it. |
status | string | Always approved in this event. |
recurring_cycle | integer or null | Billing cycle of a recurring subscription the charge pays, starting at 1. null for other charges. See Tell charges apart. |
amount_in_cents | integer | Amount charged, tax included. |
tax_in_cents | integer | Tax included in amount_in_cents. |
currency_id | string or null | ISO 4217 currency code of the charge. |
paid_at | string | ISO 8601 UTC charge date, as the gateway reported it. |
data.order, data.user and data.products
The same fields as in order.paid: the order the charge belongs to, its buyer and every product of the order, not only the ones the charge pays for.
order.status is the status the charge leaves the order in: approved when the charge approved or renewed it, or the order's current status, such as cancelled, when the order was already closed.
data.payments
The payments of this charge only, sorted by id, with the same fields as in order.paid. Earlier charges of the order are not included.
Related Documentation
- Webhooks: Endpoints, request format, signature verification and delivery.
order.paid: The event sent once per order, and the fields oforder,user,productsandpayments.- Orders API: The order types and fields this event mirrors.