snapshot of https://github.com/zulip/zulip.git @ 48c69911c7dd4e03b20552eb3bd77ad8151c1357 (2026-09-15)
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled

This commit is contained in:
upstream-mirror
2026-09-15 15:05:30 +05:00
commit 64898b2c79
8999 changed files with 1757358 additions and 0 deletions

View File

View File

@@ -0,0 +1,44 @@
# Zulip Stripe integration
Get Zulip notifications for Stripe events!
{% if 'http:' in external_url_scheme %}
!!! tip ""
Stripe will only send webhook payloads over HTTPS, but your Zulip
server is configured to only accept HTTP. To use the Stripe
webhook with this Zulip server, you will need to use a tunneling
service like ngrok or Ultrahook.
{% endif %}
{start_tabs}
1. {!create-an-incoming-webhook.md!}
1. {!generate-webhook-url-basic.md!}
1. On your Stripe Dashboard, select **Developers** on the left
sidebar. Select **Webhooks**, and click on **+ Add endpoint**.
1. Set the **URL to be called** to the URL generated above. Select the
[event types](#filtering-incoming-events) you would like to be notified
about, and click **Add endpoint**.
1. [Add a new linkifier](/help/add-a-custom-linkifier) in your Zulip
organization. Set the pattern to `(?P<id>cus_[0-9a-zA-Z]+)` and the URL
template to `https://dashboard.stripe.com/customers/{id}`. This step
creates links to Stripe customers in Zulip messages and topics.
{end_tabs}
{!congrats.md!}
![](/static/images/integrations/stripe/001.png)
{!event-filtering-additional-feature.md!}
### Related documentation
{!webhooks-url-specification.md!}

View File

@@ -0,0 +1,108 @@
{
"created": 1326853478,
"livemode": false,
"id": "evt_00000000000000",
"type": "account.updated",
"object": "event",
"request": null,
"pending_webhooks": 1,
"api_version": "2018-02-28",
"data": {
"object": {
"id": "acct_00000000000000",
"object": "account",
"business_logo": null,
"business_logo_large": null,
"business_name": null,
"business_primary_color": null,
"business_url": null,
"charges_enabled": false,
"country": "CA",
"created": 1523744779,
"debit_negative_balances": true,
"decline_charge_on": {
"avs_failure": false,
"cvc_failure": true
},
"default_currency": "cad",
"details_submitted": true,
"display_name": null,
"email": "test@stripe.com",
"external_accounts": {
"object": "list",
"data": [
],
"has_more": false,
"total_count": 0,
"url": "/v1/accounts/acct_1CGwp5HSaWXyvFpK/external_accounts"
},
"legal_entity": {
"additional_owners": [
],
"address": {
"city": null,
"country": "CA",
"line1": null,
"line2": null,
"postal_code": null,
"state": null
},
"business_name": null,
"business_tax_id_provided": false,
"dob": {
"day": null,
"month": null,
"year": null
},
"first_name": null,
"last_name": null,
"personal_address": {
"city": null,
"country": "CA",
"line1": null,
"line2": null,
"postal_code": null,
"state": null
},
"personal_id_number_provided": false,
"type": null,
"verification": {
"details": null,
"details_code": null,
"document": null,
"document_back": null,
"status": "unverified"
}
},
"mcc": null,
"metadata": {
},
"payout_schedule": {
"delay_days": 7,
"interval": "daily"
},
"payout_statement_descriptor": null,
"payouts_enabled": false,
"product_description": null,
"statement_descriptor": "TEST",
"support_address": null,
"support_email": null,
"support_phone": null,
"support_url": null,
"timezone": "America/St_Johns",
"tos_acceptance": {
"date": null,
"ip": null,
"user_agent": null
},
"type": "standard",
"verification": {
"disabled_reason": "fields_needed",
"due_by": 1554669463,
"fields_needed": [
"legal_entity.verification.document"
]
}
}
}
}

View File

@@ -0,0 +1,61 @@
{
"created": 1326853478,
"livemode": false,
"id": "evt_00000000000000",
"type": "charge.dispute.closed",
"object": "event",
"request": null,
"pending_webhooks": 1,
"api_version": "2016-07-06",
"data": {
"object": {
"id": "dp_00000000000000",
"object": "dispute",
"amount": 1001,
"balance_transactions": [],
"charge": "ch_00000000000000",
"created": 1480672052,
"currency": "aud",
"evidence": {
"access_activity_log": null,
"billing_address": null,
"cancellation_policy": null,
"cancellation_policy_disclosure": null,
"cancellation_rebuttal": null,
"customer_communication": null,
"customer_email_address": null,
"customer_name": null,
"customer_purchase_ip": null,
"customer_signature": null,
"duplicate_charge_documentation": null,
"duplicate_charge_explanation": null,
"duplicate_charge_id": null,
"product_description": null,
"receipt": null,
"refund_policy": null,
"refund_policy_disclosure": null,
"refund_refusal_explanation": null,
"service_date": null,
"service_documentation": null,
"shipping_address": null,
"shipping_carrier": null,
"shipping_date": null,
"shipping_documentation": null,
"shipping_tracking_number": null,
"uncategorized_file": null,
"uncategorized_text": "Here is some evidence"
},
"evidence_details": {
"due_by": 1482364799,
"has_evidence": false,
"past_due": false,
"submission_count": 0
},
"is_charge_refundable": false,
"livemode": false,
"metadata": {},
"reason": "general",
"status": "won"
}
}
}

View File

@@ -0,0 +1,61 @@
{
"created": 1326853478,
"livemode": false,
"id": "evt_00000000000000",
"type": "charge.dispute.created",
"object": "event",
"request": null,
"pending_webhooks": 1,
"api_version": "2016-07-06",
"data": {
"object": {
"id": "dp_00000000000000",
"object": "dispute",
"amount": 1000,
"balance_transactions": [],
"charge": "ch_00000000000000",
"created": 1480672043,
"currency": "jpy",
"evidence": {
"access_activity_log": null,
"billing_address": null,
"cancellation_policy": null,
"cancellation_policy_disclosure": null,
"cancellation_rebuttal": null,
"customer_communication": null,
"customer_email_address": null,
"customer_name": null,
"customer_purchase_ip": null,
"customer_signature": null,
"duplicate_charge_documentation": null,
"duplicate_charge_explanation": null,
"duplicate_charge_id": null,
"product_description": null,
"receipt": null,
"refund_policy": null,
"refund_policy_disclosure": null,
"refund_refusal_explanation": null,
"service_date": null,
"service_documentation": null,
"shipping_address": null,
"shipping_carrier": null,
"shipping_date": null,
"shipping_documentation": null,
"shipping_tracking_number": null,
"uncategorized_file": null,
"uncategorized_text": null
},
"evidence_details": {
"due_by": 1482364799,
"has_evidence": false,
"past_due": false,
"submission_count": 0
},
"is_charge_refundable": false,
"livemode": false,
"metadata": {},
"reason": "general",
"status": "needs_response"
}
}
}

View File

@@ -0,0 +1,76 @@
{
"created": 1326853478,
"livemode": false,
"id": "evt_00000000000000",
"type": "charge.failed",
"object": "event",
"request": null,
"pending_webhooks": 1,
"api_version": "2016-07-06",
"data": {
"object": {
"id": "ch_00000000000000",
"object": "charge",
"amount": 100,
"amount_refunded": 0,
"application": null,
"application_fee": null,
"balance_transaction": "txn_00000000000000",
"captured": true,
"created": 1480671978,
"currency": "aud",
"customer": null,
"description": "My First Test Charge (created for API docs)",
"destination": null,
"dispute": null,
"failure_code": null,
"failure_message": null,
"fraud_details": {},
"invoice": null,
"livemode": false,
"metadata": {},
"order": null,
"outcome": null,
"paid": false,
"receipt_email": null,
"receipt_number": null,
"refunded": false,
"refunds": {
"object": "list",
"data": [],
"has_more": false,
"total_count": 0,
"url": "/v1/charges/ch_19MDcUCV4wXizEw4AsxeFwTn/refunds"
},
"review": null,
"shipping": null,
"source": {
"id": "card_00000000000000",
"object": "card",
"address_city": null,
"address_country": null,
"address_line1": null,
"address_line1_check": null,
"address_line2": null,
"address_state": null,
"address_zip": null,
"address_zip_check": null,
"brand": "Visa",
"country": "US",
"customer": null,
"cvc_check": null,
"dynamic_last4": null,
"exp_month": 8,
"exp_year": 2017,
"funding": "credit",
"last4": "4242",
"metadata": {},
"name": null,
"tokenization_method": null
},
"source_transfer": null,
"statement_descriptor": null,
"status": "succeeded"
}
}
}

View File

@@ -0,0 +1,89 @@
{
"id": "evt_1DbqCuDEQaroqDjsojJSNhmh",
"pending_webhooks": 1,
"data": {
"object": {
"id": "ch_000000000000000000000000",
"object": "charge",
"amount": 100,
"amount_refunded": 0,
"application": null,
"application_fee": null,
"balance_transaction": "txn_000000000000000000000000",
"captured": true,
"created": 1542991183,
"currency": "aud",
"customer": "cus_00000000000000",
"description": null,
"destination": null,
"dispute": null,
"failure_code": null,
"failure_message": null,
"fraud_details": {
},
"invoice": "in_000000000000000000000000",
"livemode": true,
"metadata": {
},
"on_behalf_of": null,
"order": null,
"outcome": {
"network_status": "approved_by_network",
"reason": null,
"risk_level": "normal",
"seller_message": "Payment complete.",
"type": "authorized"
},
"paid": true,
"payment_intent": null,
"receipt_email": "user@example.com",
"receipt_number": "0000-0000",
"refunded": false,
"refunds": {
"object": "list",
"data": [
],
"has_more": false,
"total_count": 0,
"url": "/v1/charges/ch_000000000000000000000000/refunds"
},
"review": null,
"shipping": null,
"source": {
"id": "card_000000000000000000000000",
"object": "card",
"address_city": "city",
"address_country": "country",
"address_line1": "line1",
"address_line1_check": "unavailable",
"address_line2": null,
"address_state": "ST",
"address_zip": "00000",
"address_zip_check": "unavailable",
"brand": "Visa",
"country": "CO",
"customer": "cus_00000000000000",
"cvc_check": null,
"dynamic_last4": null,
"exp_month": 1,
"exp_year": 2023,
"fingerprint": "0000000000000000",
"funding": "credit",
"last4": "0000",
"metadata": {
},
"name": "name",
"tokenization_method": null
},
"source_transfer": null,
"statement_descriptor": "Zulip Cloud Standard",
"status": "succeeded",
"transfer_group": null
}
},
"type": "charge.succeeded",
"api_version": "2018-11-08",
"object": "event",
"livemode": true,
"created": 1543500572
}

View File

@@ -0,0 +1,117 @@
{
"id": "evt_1DbqCuDEQaroqDjsojJSNhmh",
"pending_webhooks": 1,
"data": {
"object": {
"failure_message": null,
"id": "py_000000000000000000000000",
"amount": 100,
"refunds": {
"total_count": 0,
"has_more": false,
"url": "\/v1\/charges\/py_000000000000000000000000\/refunds",
"object": "list",
"data": [
]
},
"invoice": "in_000000000000000000000000",
"application": null,
"source": {
"id": "src_000000000000000000000000",
"type": "ach_credit_transfer",
"usage": "reusable",
"amount": null,
"flow": "receiver",
"customer": "cus_00000000000000",
"livemode": true,
"object": "source",
"owner": {
"address": null,
"verified_name": null,
"email": "username@example.com",
"verified_phone": null,
"phone": null,
"verified_email": null,
"verified_address": null,
"name": null
},
"statement_descriptor": null,
"metadata": {
},
"client_secret": "secret",
"currency": "usd",
"receiver": {
"amount_received": 100,
"refund_attributes_status": "missing",
"amount_charged": 100,
"amount_returned": 0,
"refund_attributes_method": "email",
"address": "1234-1234"
},
"status": "pending",
"ach_credit_transfer": {
"refund_account_holder_name": null,
"bank_name": "WELLS FARGO BANK, N.A.",
"fingerprint": "1234",
"routing_number": "1234",
"swift_code": "WFBIUS6S",
"refund_account_holder_type": null,
"account_number": "1234",
"refund_account_number": null,
"refund_routing_number": null
},
"created": 1543500572
},
"refunded": false,
"statement_descriptor": null,
"metadata": {
},
"transfer_group": null,
"amount_refunded": 0,
"currency": "usd",
"application_fee": null,
"source_transfer": null,
"object": "charge",
"receipt_email": "username@example.com",
"receipt_number": "1234-1234",
"captured": true,
"failure_code": null,
"description": null,
"payment_intent": null,
"paid": true,
"shipping": null,
"livemode": true,
"balance_transaction": "txn_000000000000000000000000",
"outcome": {
"type": "authorized",
"network_status": "approved_by_network",
"risk_level": "not_assessed",
"seller_message": "Payment complete.",
"reason": null
},
"dispute": null,
"on_behalf_of": null,
"status": "succeeded",
"fraud_details": {
},
"order": null,
"review": null,
"customer": "cus_00000000000000",
"destination": null,
"created": 1543500571
}
},
"request": {
"id": null,
"idempotency_key": "in_000000000000000000000000"
},
"type": "charge.succeeded",
"api_version": "2018-11-08",
"object": "event",
"livemode": true,
"created": 1543500572
}

View File

@@ -0,0 +1,41 @@
{
"created": 1326853478,
"livemode": false,
"id": "evt_00000000000000",
"type": "customer.created",
"object": "event",
"request": null,
"pending_webhooks": 1,
"api_version": "2016-07-06",
"data": {
"object": {
"id": "cus_00000000000000",
"object": "customer",
"account_balance": 0,
"created": 1480672088,
"currency": "aud",
"default_source": null,
"delinquent": false,
"description": null,
"discount": null,
"email": null,
"livemode": false,
"metadata": {},
"shipping": null,
"sources": {
"object": "list",
"data": [],
"has_more": false,
"total_count": 0,
"url": "/v1/customers/cus_9fYl7K4F7pQxrY/sources"
},
"subscriptions": {
"object": "list",
"data": [],
"has_more": false,
"total_count": 0,
"url": "/v1/customers/cus_9fYl7K4F7pQxrY/subscriptions"
}
}
}
}

View File

@@ -0,0 +1,41 @@
{
"created": 1326853478,
"livemode": false,
"id": "evt_00000000000000",
"type": "customer.created",
"object": "event",
"request": null,
"pending_webhooks": 1,
"api_version": "2016-07-06",
"data": {
"object": {
"id": "cus_00000000000000",
"object": "customer",
"account_balance": 0,
"created": 1480672088,
"currency": "aud",
"default_source": null,
"delinquent": false,
"description": null,
"discount": null,
"email": "example@abc.com",
"livemode": false,
"metadata": {},
"shipping": null,
"sources": {
"object": "list",
"data": [],
"has_more": false,
"total_count": 0,
"url": "/v1/customers/cus_9fYl7K4F7pQxrY/sources"
},
"subscriptions": {
"object": "list",
"data": [],
"has_more": false,
"total_count": 0,
"url": "/v1/customers/cus_9fYl7K4F7pQxrY/subscriptions"
}
}
}
}

View File

@@ -0,0 +1,41 @@
{
"created": 1326853478,
"livemode": false,
"id": "evt_00000000000000",
"type": "customer.deleted",
"object": "event",
"request": null,
"pending_webhooks": 1,
"api_version": "2016-07-06",
"data": {
"object": {
"id": "cus_00000000000000",
"object": "customer",
"account_balance": 0,
"created": 1480672097,
"currency": "aud",
"default_source": null,
"delinquent": false,
"description": null,
"discount": null,
"email": null,
"livemode": false,
"metadata": {},
"shipping": null,
"sources": {
"object": "list",
"data": [],
"has_more": false,
"total_count": 0,
"url": "/v1/customers/cus_9fYlWRryEw2I9B/sources"
},
"subscriptions": {
"object": "list",
"data": [],
"has_more": false,
"total_count": 0,
"url": "/v1/customers/cus_9fYlWRryEw2I9B/subscriptions"
}
}
}
}

View File

@@ -0,0 +1,39 @@
{
"created":1326853478,
"livemode":false,
"id":"customer.discount.created_00000000000000",
"type":"customer.discount.created",
"object":"event",
"request":null,
"pending_webhooks":1,
"api_version":"2018-02-28",
"data":{
"object":{
"object":"discount",
"coupon":{
"id":"25_00000000000000",
"object":"coupon",
"amount_off":null,
"created":1543438043,
"currency":null,
"duration":"repeating",
"duration_in_months":3,
"livemode":false,
"max_redemptions":null,
"metadata":{
},
"name":"25.5% off",
"percent_off":26,
"percent_off_precise":25.5,
"redeem_by":null,
"times_redeemed":0,
"valid":true
},
"customer":"cus_00000000000000",
"end":1551386843,
"start":1543438043,
"subscription":null
}
}
}

View File

@@ -0,0 +1,102 @@
{
"id": "evt_1DeFYZDEQaroqDjsX811NXeq",
"object": "event",
"api_version": "2018-11-08",
"created": 1544074671,
"data": {
"object": {
"id": "sub_E6STM5w5EX3K28",
"object": "subscription",
"application_fee_percent": null,
"billing": "send_invoice",
"billing_cycle_anchor": 1544074670,
"cancel_at_period_end": false,
"canceled_at": null,
"created": 1544074670,
"current_period_end": 1575610670,
"current_period_start": 1544074670,
"customer": "cus_00000000000000",
"days_until_due": 7,
"default_source": null,
"discount": null,
"ended_at": null,
"items": {
"object": "list",
"data": [
{
"id": "si_E6STR3cU0bwWFG",
"object": "subscription_item",
"created": 1544074671,
"metadata": {
},
"plan": {
"id": "plan_E6SQ6RAtmLVtzg",
"object": "plan",
"active": true,
"aggregate_usage": null,
"amount": 100,
"billing_scheme": "per_unit",
"created": 1544074513,
"currency": "usd",
"interval": "year",
"interval_count": 1,
"livemode": true,
"metadata": {
},
"nickname": "flatrate",
"product": "prod_E6SPZ5RKfKClNL",
"tiers": null,
"tiers_mode": null,
"transform_usage": null,
"trial_period_days": null,
"usage_type": "licensed"
},
"quantity": 800,
"subscription": "sub_E6STM5w5EX3K28"
}
],
"has_more": false,
"total_count": 1,
"url": "/v1/subscription_items?subscription=sub_E6STM5w5EX3K28"
},
"livemode": true,
"metadata": {
},
"plan": {
"id": "plan_E6SQ6RAtmLVtzg",
"object": "plan",
"active": true,
"aggregate_usage": null,
"amount": 100,
"billing_scheme": "per_unit",
"created": 1544074513,
"currency": "usd",
"interval": "year",
"interval_count": 1,
"livemode": true,
"metadata": {
},
"nickname": "flatrate",
"product": "prod_E6SPZ5RKfKClNL",
"tiers": null,
"tiers_mode": null,
"transform_usage": null,
"trial_period_days": null,
"usage_type": "licensed"
},
"quantity": 800,
"start": 1544074670,
"status": "active",
"tax_percent": null,
"trial_end": null,
"trial_start": null
}
},
"livemode": true,
"pending_webhooks": 1,
"request": {
"id": "req_J85F0AkACDDTBU",
"idempotency_key": null
},
"type": "customer.subscription.created"
}

View File

@@ -0,0 +1,102 @@
{
"id": "evt_1DeFYZDEQaroqDjsX811NXeq",
"object": "event",
"api_version": "2018-11-08",
"created": 1544074671,
"data": {
"object": {
"id": "sub_E6STM5w5EX3K28",
"object": "subscription",
"application_fee_percent": null,
"billing": "send_invoice",
"billing_cycle_anchor": 1544074670,
"cancel_at_period_end": false,
"canceled_at": null,
"created": 1544074670,
"current_period_end": 1575610670,
"current_period_start": 1544074670,
"customer": "cus_00000000000000",
"days_until_due": 7,
"default_source": null,
"discount": null,
"ended_at": null,
"items": {
"object": "list",
"data": [
{
"id": "si_E6STR3cU0bwWFG",
"object": "subscription_item",
"created": 1544074671,
"metadata": {
},
"plan": {
"id": "plan_E6SQ6RAtmLVtzg",
"object": "plan",
"active": true,
"aggregate_usage": null,
"amount": 100,
"billing_scheme": "per_unit",
"created": 1544074513,
"currency": "usd",
"interval": "year",
"interval_count": 1,
"livemode": true,
"metadata": {
},
"nickname": null,
"product": "prod_E6SPZ5RKfKClNL",
"tiers": null,
"tiers_mode": null,
"transform_usage": null,
"trial_period_days": null,
"usage_type": "licensed"
},
"quantity": 800,
"subscription": "sub_E6STM5w5EX3K28"
}
],
"has_more": false,
"total_count": 1,
"url": "/v1/subscription_items?subscription=sub_E6STM5w5EX3K28"
},
"livemode": true,
"metadata": {
},
"plan": {
"id": "plan_E6SQ6RAtmLVtzg",
"object": "plan",
"active": true,
"aggregate_usage": null,
"amount": 100,
"billing_scheme": "per_unit",
"created": 1544074513,
"currency": "usd",
"interval": "year",
"interval_count": 1,
"livemode": true,
"metadata": {
},
"nickname": null,
"product": "prod_E6SPZ5RKfKClNL",
"tiers": null,
"tiers_mode": null,
"transform_usage": null,
"trial_period_days": null,
"usage_type": "licensed"
},
"quantity": 800,
"start": 1544074670,
"status": "active",
"tax_percent": null,
"trial_end": null,
"trial_start": null
}
},
"livemode": true,
"pending_webhooks": 1,
"request": {
"id": "req_J85F0AkACDDTBU",
"idempotency_key": null
},
"type": "customer.subscription.created"
}

View File

@@ -0,0 +1,47 @@
{
"created": 1326853478,
"livemode": false,
"id": "evt_00000000000000",
"type": "customer.subscription.deleted",
"object": "event",
"request": null,
"pending_webhooks": 1,
"api_version": "2016-07-06",
"data": {
"object": {
"id": "sub_00000000000000",
"object": "subscription",
"application_fee_percent": null,
"cancel_at_period_end": false,
"canceled_at": null,
"created": 1480672113,
"current_period_end": 1483350513,
"current_period_start": 1480672113,
"customer": "cus_00000000000000",
"discount": null,
"ended_at": 1480633660,
"livemode": false,
"metadata": {},
"plan": {
"id": "gold_00000000000000",
"object": "plan",
"amount": 2000,
"created": 1394782886,
"currency": "aud",
"interval": "month",
"interval_count": 1,
"livemode": false,
"metadata": {},
"name": "Gold Special",
"statement_descriptor": null,
"trial_period_days": null
},
"quantity": 1,
"start": 1480672113,
"status": "canceled",
"tax_percent": null,
"trial_end": null,
"trial_start": null
}
}
}

View File

@@ -0,0 +1,47 @@
{
"created": 1326853478,
"livemode": false,
"id": "evt_00000000000000",
"type": "customer.subscription.trial_will_end",
"object": "event",
"request": null,
"pending_webhooks": 1,
"api_version": "2016-07-06",
"data": {
"object": {
"id": "sub_00000000000000",
"object": "subscription",
"application_fee_percent": null,
"cancel_at_period_end": false,
"canceled_at": null,
"created": 1480672121,
"current_period_end": 1483350521,
"current_period_start": 1480672121,
"customer": "cus_00000000000000",
"discount": null,
"ended_at": null,
"livemode": false,
"metadata": {},
"plan": {
"id": "gold_00000000000000",
"object": "plan",
"amount": 2000,
"created": 1394782886,
"currency": "aud",
"interval": "month",
"interval_count": 1,
"livemode": false,
"metadata": {},
"name": "Gold Special",
"statement_descriptor": null,
"trial_period_days": null
},
"quantity": 1,
"start": 1480672121,
"status": "trialing",
"tax_percent": null,
"trial_end": 1480892861,
"trial_start": 1480633661
}
}
}

View File

@@ -0,0 +1,111 @@
{
"id": "evt_1DeFo8DEQaroqDjsk4tzzzdV",
"object": "event",
"api_version": "2018-11-08",
"created": 1544075636,
"data": {
"object": {
"id": "sub_E6STM5w5EX3K28",
"object": "subscription",
"application_fee_percent": null,
"billing": "send_invoice",
"billing_cycle_anchor": 1572609600,
"cancel_at_period_end": false,
"canceled_at": null,
"created": 1544074670,
"current_period_end": 1572609600,
"current_period_start": 1544075635,
"customer": "cus_00000000000000",
"days_until_due": 7,
"default_source": null,
"discount": null,
"ended_at": null,
"items": {
"object": "list",
"data": [
{
"id": "si_E6STR3cU0bwWFG",
"object": "subscription_item",
"created": 1544074671,
"metadata": {
},
"plan": {
"id": "plan_E6SQ6RAtmLVtzg",
"object": "plan",
"active": true,
"aggregate_usage": null,
"amount": 100,
"billing_scheme": "per_unit",
"created": 1544074513,
"currency": "usd",
"interval": "year",
"interval_count": 1,
"livemode": true,
"metadata": {
},
"nickname": "flatrate",
"product": "prod_E6SPZ5RKfKClNL",
"tiers": null,
"tiers_mode": null,
"transform_usage": null,
"trial_period_days": null,
"usage_type": "licensed"
},
"quantity": 800,
"subscription": "sub_E6STM5w5EX3K28"
}
],
"has_more": false,
"total_count": 1,
"url": "/v1/subscription_items?subscription=sub_E6STM5w5EX3K28"
},
"livemode": true,
"metadata": {
},
"plan": {
"id": "plan_E6SQ6RAtmLVtzg",
"object": "plan",
"active": true,
"aggregate_usage": null,
"amount": 100,
"billing_scheme": "per_unit",
"created": 1544074513,
"currency": "usd",
"interval": "year",
"interval_count": 1,
"livemode": true,
"metadata": {
},
"nickname": "flatrate",
"product": "prod_E6SPZ5RKfKClNL",
"tiers": null,
"tiers_mode": null,
"transform_usage": null,
"trial_period_days": null,
"usage_type": "licensed"
},
"quantity": 800,
"start": 1544075635,
"status": "trialing",
"tax_percent": null,
"trial_end": 1572609600,
"trial_start": 1544075635
},
"previous_attributes": {
"billing_cycle_anchor": 1544074670,
"current_period_end": 1575610670,
"current_period_start": 1544074670,
"start": 1544074670,
"status": "active",
"trial_end": null,
"trial_start": null
}
},
"livemode": true,
"pending_webhooks": 1,
"request": {
"id": "req_X3p7qbGSX2wZ8D",
"idempotency_key": null
},
"type": "customer.subscription.updated"
}

View File

@@ -0,0 +1,188 @@
{
"id": "evt_1DbqCuDEQaroqDjsIkZIT6uP",
"object": "event",
"api_version": "2018-11-08",
"created": 1543500572,
"data": {
"object": {
"id": "cus_00000000000000",
"object": "customer",
"account_balance": 100,
"created": 1543971209,
"currency": "usd",
"default_source": "src_1DdoduGh0CmXqmnwKJmq8lpv",
"delinquent": false,
"description": "zulip (Zulip Dev)",
"discount": null,
"email": "hamlet@zulip.com",
"invoice_prefix": "6E2831C",
"livemode": false,
"metadata": {
"realm_id": "2",
"realm_str": "zulip"
},
"shipping": null,
"sources": {
"object": "list",
"data": [
{
"id": "src_1DdoduGh0CmXqmnwKJmq8lpv",
"object": "source",
"ach_credit_transfer": {
"account_number": "test_a479e261d1bb",
"bank_name": "TEST BANK",
"fingerprint": "cAzkzdbKgZYS8NhC",
"routing_number": "110000000",
"swift_code": "TSTEZ122",
"refund_routing_number": null,
"refund_account_number": null,
"refund_account_holder_type": null,
"refund_account_holder_name": null
},
"amount": null,
"client_secret": "src_client_secret_E60fLRPcrzhAJNmOjvsUHTli",
"created": 1543971214,
"currency": "usd",
"customer": "cus_E60fF1CuG8K36D",
"flow": "receiver",
"livemode": false,
"metadata": {
},
"owner": {
"address": null,
"email": "amount_0@stripe.com",
"name": null,
"phone": null,
"verified_address": null,
"verified_email": null,
"verified_name": null,
"verified_phone": null
},
"receiver": {
"address": "110000000-test_a479e261d1bb",
"amount_charged": 0,
"amount_received": 0,
"amount_returned": 0,
"refund_attributes_method": "email",
"refund_attributes_status": "missing"
},
"statement_descriptor": null,
"status": "pending",
"type": "ach_credit_transfer",
"usage": "reusable"
}
],
"has_more": false,
"total_count": 1,
"url": "/v1/customers/cus_E60fF1CuG8K36D/sources"
},
"subscriptions": {
"object": "list",
"data": [
{
"id": "sub_E60fxv28JMUWOG",
"object": "subscription",
"application_fee_percent": null,
"billing": "send_invoice",
"billing_cycle_anchor": 1543971209,
"cancel_at_period_end": false,
"canceled_at": null,
"created": 1543971209,
"current_period_end": 1575507209,
"current_period_start": 1543971209,
"customer": "cus_E60fF1CuG8K36D",
"days_until_due": 30,
"default_source": null,
"discount": null,
"ended_at": null,
"items": {
"object": "list",
"data": [
{
"id": "si_E60fDuBL4e55ZF",
"object": "subscription_item",
"created": 1543971210,
"metadata": {
},
"plan": {
"id": "plan_Do3xCvbzO89OsR",
"object": "plan",
"active": true,
"aggregate_usage": null,
"amount": 8000,
"billing_scheme": "per_unit",
"created": 1539831971,
"currency": "usd",
"interval": "year",
"interval_count": 1,
"livemode": false,
"metadata": {
},
"nickname": "annual",
"product": "prod_Do3x494SetTDpx",
"tiers": null,
"tiers_mode": null,
"transform_usage": null,
"trial_period_days": null,
"usage_type": "licensed"
},
"quantity": 8,
"subscription": "sub_E60fxv28JMUWOG"
}
],
"has_more": false,
"total_count": 1,
"url": "/v1/subscription_items?subscription=sub_E60fxv28JMUWOG"
},
"livemode": false,
"metadata": {
},
"plan": {
"id": "plan_Do3xCvbzO89OsR",
"object": "plan",
"active": true,
"aggregate_usage": null,
"amount": 8000,
"billing_scheme": "per_unit",
"created": 1539831971,
"currency": "usd",
"interval": "year",
"interval_count": 1,
"livemode": false,
"metadata": {
},
"nickname": "annual",
"product": "prod_Do3x494SetTDpx",
"tiers": null,
"tiers_mode": null,
"transform_usage": null,
"trial_period_days": null,
"usage_type": "licensed"
},
"quantity": 8,
"start": 1543971210,
"status": "active",
"tax_percent": 0,
"trial_end": null,
"trial_start": null
}
],
"has_more": false,
"total_count": 1,
"url": "/v1/customers/cus_E60fF1CuG8K36D/subscriptions"
},
"tax_info": null,
"tax_info_verification": null
},
"previous_attributes": {
"account_balance": 0
}
},
"livemode": true,
"pending_webhooks": 1,
"request": {
"id": null,
"idempotency_key": null
},
"type": "customer.updated"
}

View File

@@ -0,0 +1,140 @@
{
"id": "evt_1GpmuvHLwdCOCoR7Q22hCa2N",
"object": "event",
"api_version": "2020-03-02",
"created": 1591153524,
"data": {
"object": {
"id": "in_1GpmuuHLwdCOCoR7ghzQDQLW",
"object": "invoice",
"account_country": "IN",
"account_name": null,
"amount_due": 0,
"amount_paid": 0,
"amount_remaining": 0,
"application_fee_amount": null,
"attempt_count": 0,
"attempted": false,
"auto_advance": false,
"billing_reason": "manual",
"charge": null,
"collection_method": "send_invoice",
"created": 1591153524,
"currency": "inr",
"custom_fields": null,
"customer": "cus_HH97asvHvaYQYp",
"customer_address": null,
"customer_email": "zenitsu_agatsuma@mail.example.com",
"customer_name": "Zenitsu Agatsuma",
"customer_phone": null,
"customer_shipping": {
"address": {
"city": "",
"country": "",
"line1": "",
"line2": "",
"postal_code": "",
"state": ""
},
"name": "Zenitsu Agatsuma",
"phone": ""
},
"customer_tax_exempt": "none",
"customer_tax_ids": [],
"default_payment_method": null,
"default_source": null,
"default_tax_rates": [],
"description": null,
"discount": null,
"due_date": null,
"ending_balance": null,
"footer": null,
"hosted_invoice_url": null,
"invoice_pdf": null,
"lines": {
"object": "list",
"data": [
{
"id": "il_1GpmuuHLwdCOCoR7nCRM5MXl",
"object": "line_item",
"amount": 0,
"currency": "inr",
"description": null,
"discountable": true,
"invoice_item": "ii_1GpmuuHLwdCOCoR7VOeTr2BM",
"livemode": false,
"metadata": {},
"period": {
"end": 1591153524,
"start": 1591153524
},
"plan": null,
"price": {
"id": "price_1GpmuuHLwdCOCoR7ff5A7n1i",
"object": "price",
"active": false,
"billing_scheme": "per_unit",
"created": 1591153524,
"currency": "inr",
"livemode": false,
"lookup_key": null,
"metadata": {},
"nickname": null,
"product": "prod_HOa5MrXjZ9YBuA",
"recurring": null,
"tiers_mode": null,
"transform_quantity": null,
"type": "one_time",
"unit_amount": 0,
"unit_amount_decimal": "0"
},
"proration": false,
"quantity": 1,
"subscription": null,
"tax_amounts": [],
"tax_rates": [],
"type": "invoiceitem"
}
],
"has_more": false,
"total_count": 1,
"url": "/v1/invoices/in_1GpmuuHLwdCOCoR7ghzQDQLW/lines"
},
"livemode": false,
"metadata": {},
"next_payment_attempt": null,
"number": null,
"paid": false,
"payment_intent": null,
"period_end": 1591153524,
"period_start": 1591153524,
"post_payment_credit_notes_amount": 0,
"pre_payment_credit_notes_amount": 0,
"receipt_number": null,
"starting_balance": 0,
"statement_descriptor": null,
"status": "draft",
"status_transitions": {
"finalized_at": null,
"marked_uncollectible_at": null,
"paid_at": null,
"voided_at": null
},
"subscription": null,
"subtotal": 0,
"tax": null,
"tax_percent": null,
"total": 0,
"total_tax_amounts": [],
"transfer_data": null,
"webhooks_delivered_at": null
}
},
"livemode": false,
"pending_webhooks": 1,
"request": {
"id": "req_cz0vh1VJ1U4MzE",
"idempotency_key": null
},
"type": "invoice.created"
}

View File

@@ -0,0 +1,83 @@
{
"created": 1326853478,
"livemode": false,
"id": "evt_00000000000000",
"type": "invoice.payment_failed",
"object": "event",
"request": null,
"pending_webhooks": 1,
"api_version": "2016-07-06",
"data": {
"object": {
"id": "in_00000000000000",
"object": "invoice",
"amount_due": 0,
"application_fee": null,
"attempt_count": 0,
"attempted": true,
"charge": null,
"closed": false,
"currency": "aud",
"customer": "cus_00000000000000",
"date": 1480672129,
"description": null,
"discount": null,
"ending_balance": null,
"forgiven": false,
"lines": {
"data": [
{
"id": "sub_9fYmJQOQy04yoh",
"object": "line_item",
"amount": 2000,
"currency": "aud",
"description": null,
"discountable": true,
"livemode": true,
"metadata": {},
"period": {
"start": 1483350529,
"end": 1486028929
},
"plan": {
"id": "gold",
"object": "plan",
"amount": 2000,
"created": 1480672129,
"currency": "aud",
"interval": "month",
"interval_count": 1,
"livemode": false,
"metadata": {},
"name": "Gold Special",
"statement_descriptor": null,
"trial_period_days": null
},
"proration": false,
"quantity": 1,
"subscription": null,
"type": "subscription"
}
],
"total_count": 1,
"object": "list",
"url": "/v1/invoices/in_19MDevCV4wXizEw4B4lY7YPR/lines"
},
"livemode": false,
"metadata": {},
"next_payment_attempt": 1480675729,
"paid": false,
"period_end": 1480672129,
"period_start": 1480672129,
"receipt_number": null,
"starting_balance": 0,
"statement_descriptor": null,
"subscription": null,
"subtotal": 0,
"tax": null,
"tax_percent": null,
"total": 0,
"webhooks_delivered_at": null
}
}
}

View File

@@ -0,0 +1,149 @@
{
"object": "event",
"type": "invoice.updated",
"pending_webhooks": 1,
"created": 1560063585,
"livemode": false,
"request": {
"idempotency_key": null,
"id": "req_wRT7KgbXvlQMfC"
},
"data": {
"previous_attributes": {
"status": "draft",
"due_date": null,
"invoice_pdf": null,
"status_transitions": {
"finalized_at": null
},
"number": "B7C8CA7D-DRAFT",
"ending_balance": null,
"payment_intent": null,
"hosted_invoice_url": null
},
"object": {
"default_tax_rates": [],
"livemode": false,
"tax": null,
"number": "B7C8CA7D-0001",
"attempt_count": 0,
"currency": "usd",
"payment_intent": "pi_1EjL0HHuGUuNWDDZ6TdrsWzQ",
"default_payment_method": null,
"total": 3000,
"account_name": null,
"custom_fields": [
{
"name": "Custom field",
"value": "test value"
}
],
"customer_name": "Test Customer",
"customer_shipping": null,
"customer_tax_ids": [],
"invoice_pdf": "https://pay.stripe.com/invoice/invst_444pftvnD8HFMJCXHn6TQKTDMc/pdf",
"billing": "send_invoice",
"footer": "Some random help and legal information.",
"description": "Thank you for your business!",
"customer_phone": null,
"id": "in_1EjKuzHuGUuNWDDZmFUP3FxU",
"webhooks_delivered_at": 1560063257,
"charge": null,
"period_end": 1560063257,
"discount": null,
"customer_tax_exempt": "none",
"post_payment_credit_notes_amount": 0,
"metadata": {},
"status": "open",
"due_date": 1562655586,
"pre_payment_credit_notes_amount": 0,
"customer_email": "test_customer@test.gmail.com",
"collection_method": "send_invoice",
"statement_descriptor": null,
"attempted": false,
"amount_paid": 0,
"object": "invoice",
"amount_remaining": 3000,
"paid": false,
"customer_address": null,
"auto_advance": true,
"ending_balance": 0,
"billing_reason": "manual",
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_444pftvnD8HFMJCXHn6TQKTDMc",
"period_start": 1560063257,
"subtotal": 3000,
"tax_percent": null,
"subscription": null,
"customer": "cus_FDmT2sIGjFskP2",
"total_tax_amounts": [],
"application_fee_amount": null,
"next_payment_attempt": null,
"created": 1560063257,
"status_transitions": {
"voided_at": null,
"finalized_at": 1560063585,
"marked_uncollectible_at": null,
"paid_at": null
},
"default_source": null,
"lines": {
"has_more": false,
"total_count": 2,
"object": "list",
"data": [
{
"discountable": true,
"livemode": false,
"description": "Watermelons",
"subscription": null,
"proration": false,
"object": "line_item",
"period": {
"start": 1560063400,
"end": 1560063400
},
"tax_rates": [],
"currency": "usd",
"amount": 1000,
"invoice_item": "ii_1EjKxIHuGUuNWDDZhiVHinLl",
"tax_amounts": [],
"plan": null,
"quantity": 2,
"type": "invoiceitem",
"id": "ii_1EjKxIHuGUuNWDDZhiVHinLl",
"metadata": {}
},
{
"discountable": true,
"livemode": false,
"description": "Apples",
"subscription": null,
"proration": false,
"object": "line_item",
"period": {
"start": 1560063257,
"end": 1560063257
},
"tax_rates": [],
"currency": "usd",
"amount": 2000,
"invoice_item": "ii_1EjKuzHuGUuNWDDZKrPUb2KA",
"tax_amounts": [],
"plan": null,
"quantity": 10,
"type": "invoiceitem",
"id": "ii_1EjKuzHuGUuNWDDZKrPUb2KA",
"metadata": {}
}
],
"url": "/v1/invoices/in_1EjKuzHuGUuNWDDZmFUP3FxU/lines"
},
"account_country": "US",
"starting_balance": 0,
"amount_due": 3000,
"receipt_number": null
}
},
"id": "evt_1EjL0IHuGUuNWDDZCFLOYrNv",
"api_version": "2019-03-14"
}

View File

@@ -0,0 +1,125 @@
{
"object": "event",
"type": "invoice.updated",
"pending_webhooks": 1,
"created": 1560064810,
"livemode": false,
"request": {
"idempotency_key": null,
"id": "req_4ITsSSm2tprAwz"
},
"data": {
"previous_attributes": {
"status": "draft",
"due_date": null,
"invoice_pdf": null,
"attempted": false,
"number": "113948B9-DRAFT",
"ending_balance": null,
"auto_advance": true,
"paid": false,
"status_transitions": {
"finalized_at": null,
"paid_at": null
},
"hosted_invoice_url": null
},
"object": {
"default_tax_rates": [],
"livemode": false,
"tax": null,
"number": "113948B9-0001",
"attempt_count": 0,
"currency": "usd",
"payment_intent": null,
"default_payment_method": null,
"total": 100,
"account_name": null,
"custom_fields": null,
"customer_name": "Hemanth V. Alluri",
"customer_shipping": null,
"customer_tax_ids": [],
"invoice_pdf": "https://pay.stripe.com/invoice/invst_iTAW0hYwxklggGxPhtXkSyUF8y/pdf",
"billing": "send_invoice",
"footer": null,
"description": null,
"customer_phone": null,
"id": "in_1EjLINHuGUuNWDDZjDf2WNqd",
"webhooks_delivered_at": 1560064708,
"charge": null,
"period_end": 1560064707,
"discount": null,
"customer_tax_exempt": "none",
"post_payment_credit_notes_amount": 0,
"metadata": {},
"status": "paid",
"due_date": 1560151211,
"pre_payment_credit_notes_amount": 0,
"customer_email": "hemanth.alluri.123@gmail.com",
"collection_method": "send_invoice",
"statement_descriptor": null,
"attempted": true,
"amount_paid": 100,
"object": "invoice",
"amount_remaining": 0,
"paid": true,
"customer_address": null,
"auto_advance": false,
"ending_balance": 0,
"billing_reason": "manual",
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_iTAW0hYwxklggGxPhtXkSyUF8y",
"period_start": 1560064707,
"subtotal": 100,
"tax_percent": null,
"subscription": null,
"customer": "cus_FDmrSwQt9Fck5M",
"total_tax_amounts": [],
"application_fee_amount": null,
"next_payment_attempt": null,
"created": 1560064707,
"status_transitions": {
"voided_at": null,
"finalized_at": 1560064810,
"marked_uncollectible_at": null,
"paid_at": 1560064810
},
"default_source": null,
"lines": {
"has_more": false,
"total_count": 1,
"object": "list",
"data": [
{
"discountable": true,
"livemode": false,
"description": "Pen",
"subscription": null,
"proration": false,
"object": "line_item",
"period": {
"start": 1560064707,
"end": 1560064707
},
"tax_rates": [],
"currency": "usd",
"amount": 100,
"invoice_item": "ii_1EjLINHuGUuNWDDZJX6Nl48C",
"tax_amounts": [],
"plan": null,
"quantity": 1,
"type": "invoiceitem",
"id": "ii_1EjLINHuGUuNWDDZJX6Nl48C",
"metadata": {}
}
],
"url": "/v1/invoices/in_1EjLINHuGUuNWDDZjDf2WNqd/lines"
},
"account_country": "US",
"starting_balance": 0,
"amount_due": 100,
"receipt_number": null
}
},
"id": "evt_1EjLK2HuGUuNWDDZZ88XyWhO",
"api_version": "2019-03-14"
}

View File

@@ -0,0 +1,35 @@
{
"created": 1326853478,
"livemode": false,
"id": "invoiceitem.created_00000000000000",
"type": "invoiceitem.created",
"object": "event",
"request": null,
"pending_webhooks": 1,
"api_version": "2018-02-28",
"data": {
"object": {
"id": "ii_00000000000000",
"object": "invoiceitem",
"amount": 1000,
"currency": "cad",
"customer": "cus_00000000000000",
"date": 1549404213,
"description": "My First Invoice Item (created for API docs)",
"discountable": true,
"invoice": null,
"livemode": false,
"metadata": {
},
"period": {
"end": 1549404213,
"start": 1549404213
},
"plan": null,
"proration": false,
"quantity": 1,
"subscription": null,
"unit_amount": 1000
}
}
}

View File

@@ -0,0 +1,41 @@
{
"object": "event",
"type": "invoiceitem.updated",
"pending_webhooks": 1,
"created": 1560063405,
"livemode": false,
"request": {
"idempotency_key": null,
"id": "req_9TWblyRuNHDMEr"
},
"data": {
"previous_attributes": {
"description": null
},
"object": {
"customer": "cus_FDmT2sIGjFskP2",
"discountable": true,
"livemode": false,
"description": "Watermelons",
"subscription": null,
"proration": false,
"object": "invoiceitem",
"period": {
"start": 1560063400,
"end": 1560063400
},
"tax_rates": [],
"currency": "usd",
"amount": 0,
"plan": null,
"invoice": "in_1EjKuzHuGUuNWDDZmFUP3FxU",
"date": 1560063400,
"unit_amount": 0,
"quantity": 1,
"id": "ii_1EjKxIHuGUuNWDDZhiVHinLl",
"metadata": {}
}
},
"id": "evt_1EjKxNHuGUuNWDDZXVzriO4I",
"api_version": "2019-03-14"
}

View File

@@ -0,0 +1,41 @@
{
"object": "event",
"type": "invoiceitem.updated",
"pending_webhooks": 1,
"created": 1560063407,
"livemode": false,
"request": {
"idempotency_key": null,
"id": "req_AApRWrApSzs6rP"
},
"data": {
"previous_attributes": {
"quantity": 1
},
"object": {
"customer": "cus_FDmT2sIGjFskP2",
"discountable": true,
"livemode": false,
"description": "Watermelons",
"subscription": null,
"proration": false,
"object": "invoiceitem",
"period": {
"start": 1560063400,
"end": 1560063400
},
"tax_rates": [],
"currency": "usd",
"amount": 0,
"plan": null,
"invoice": "in_1EjKuzHuGUuNWDDZmFUP3FxU",
"date": 1560063400,
"unit_amount": 0,
"quantity": 0,
"id": "ii_1EjKxIHuGUuNWDDZhiVHinLl",
"metadata": {}
}
},
"id": "evt_1EjKxPHuGUuNWDDZvbb9RsLf",
"api_version": "2019-03-14"
}

View File

@@ -0,0 +1,32 @@
{
"id": "evt_abcde12345ABCDE",
"object": "event",
"api_version": "2018-05-21",
"created": 1581391347,
"data": {
"object": {
"id": "pyr_abcde12345ABCDF",
"object": "refund",
"amount": 1234,
"balance_transaction": null,
"charge": "py_abcde12345ABCDG",
"created": 1589212391,
"currency": "eur",
"metadata": {},
"payment_intent": "pi_abcd1234ABCDH",
"reason": null,
"receipt_number": null,
"source_transfer_reversal": null,
"status": "succeeded",
"transfer_reversal": null
},
"previous_attributes": {}
},
"livemode": true,
"pending_webhooks": 1,
"request": {
"id": null,
"idempotency_key": null
},
"type": "charge.refund.updated"
}

View File

@@ -0,0 +1,38 @@
{
"id": "evt_1Gib6dHLwdCOCoR7mbMhSVu0",
"object": "event",
"api_version": "2020-03-02",
"created": 1589439827,
"data": {
"object": {
"id": "re_1Gib6ZHLwdCOCoR7VrzCnXlj",
"object": "refund",
"amount": 30000000,
"balance_transaction": "txn_1Gib6ZHLwdCOCoR71Gd83huz",
"charge": "ch_1Gib61HLwdCOCoR71rnkccye",
"created": 1589439823,
"currency": "inr",
"failure_balance_transaction": "txn_1Gib6dHLwdCOCoR7VQXTaZVj",
"failure_reason": "expired_or_canceled_card",
"metadata": {},
"payment_intent": "pi_1Gib60HLwdCOCoR7KJbTO3U7",
"reason": null,
"receipt_number": null,
"source_transfer_reversal": null,
"status": "failed",
"transfer_reversal": null
},
"previous_attributes": {
"failure_balance_transaction": null,
"failure_reason": null,
"status": "succeeded"
}
},
"livemode": false,
"pending_webhooks": 1,
"request": {
"id": null,
"idempotency_key": null
},
"type": "charge.refund.updated"
}

View File

@@ -0,0 +1,268 @@
from unittest import mock
from unittest.mock import MagicMock, patch
import orjson
from zerver.lib.test_classes import WebhookTestCase
class StripeHookTests(WebhookTestCase):
def test_charge_dispute_closed(self) -> None:
expected_topic_name = "disputes"
expected_message = "[Dispute](https://dashboard.stripe.com/disputes/dp_00000000000000) closed. Current status: won."
self.check_webhook(
"charge_dispute_closed",
expected_topic_name,
expected_message,
content_type="application/x-www-form-urlencoded",
)
def test_charge_dispute_created(self) -> None:
expected_topic_name = "disputes"
expected_message = "[Dispute](https://dashboard.stripe.com/disputes/dp_00000000000000) created. Current status: needs response."
self.check_webhook(
"charge_dispute_created",
expected_topic_name,
expected_message,
content_type="application/x-www-form-urlencoded",
)
def test_charge_dispute_created_pdp_prefix(self) -> None:
self.subscribe(self.test_user, self.channel_name)
payload = orjson.loads(self.get_body("charge_dispute_created"))
payload["data"]["object"]["id"] = "pdp_00000000000000"
msg = self.send_webhook_payload(
self.test_user,
self.url,
orjson.dumps(payload).decode(),
content_type="application/x-www-form-urlencoded",
)
self.assert_channel_message(
message=msg,
channel_name=self.channel_name,
topic_name="disputes",
content="[Dispute](https://dashboard.stripe.com/disputes/pdp_00000000000000) created. Current status: needs response.",
)
def test_charge_failed(self) -> None:
expected_topic_name = "charges"
expected_message = (
"[Charge](https://dashboard.stripe.com/charges/ch_00000000000000) for 1.00 AUD failed"
)
self.check_webhook(
"charge_failed",
expected_topic_name,
expected_message,
content_type="application/x-www-form-urlencoded",
)
# Credit card charge
def test_charge_succeeded__card(self) -> None:
expected_topic_name = "cus_00000000000000"
expected_message = "[Charge](https://dashboard.stripe.com/charges/ch_000000000000000000000000) for 1.00 AUD succeeded"
self.check_webhook(
"charge_succeeded__card",
expected_topic_name,
expected_message,
content_type="application/x-www-form-urlencoded",
)
# ACH payment (really a 'payment', rather than a 'charge')
def test_charge_succeeded__invoice(self) -> None:
expected_topic_name = "cus_00000000000000"
expected_message = "[Payment](https://dashboard.stripe.com/payments/py_000000000000000000000000) for $1.00 succeeded"
self.check_webhook(
"charge_succeeded__invoice",
expected_topic_name,
expected_message,
content_type="application/x-www-form-urlencoded",
)
def test_customer_created(self) -> None:
expected_topic_name = "cus_00000000000000"
expected_message = (
"[Customer](https://dashboard.stripe.com/customers/cus_00000000000000) created"
)
self.check_webhook(
"customer_created",
expected_topic_name,
expected_message,
content_type="application/x-www-form-urlencoded",
)
def test_customer_created_email(self) -> None:
expected_topic_name = "cus_00000000000000"
expected_message = "[Customer](https://dashboard.stripe.com/customers/cus_00000000000000) created\nEmail: example@abc.com"
self.check_webhook(
"customer_created_email",
expected_topic_name,
expected_message,
content_type="application/x-www-form-urlencoded",
)
def test_customer_deleted(self) -> None:
expected_topic_name = "cus_00000000000000"
expected_message = (
"[Customer](https://dashboard.stripe.com/customers/cus_00000000000000) deleted"
)
self.check_webhook(
"customer_deleted",
expected_topic_name,
expected_message,
content_type="application/x-www-form-urlencoded",
)
def test_customer_subscription_created(self) -> None:
expected_topic_name = "cus_00000000000000"
expected_message = """\
[Subscription](https://dashboard.stripe.com/subscriptions/sub_E6STM5w5EX3K28) created
Plan: [flatrate](https://dashboard.stripe.com/plans/plan_E6SQ6RAtmLVtzg)
Quantity: 800
Billing method: send invoice"""
self.check_webhook(
"customer_subscription_created",
expected_topic_name,
expected_message,
content_type="application/x-www-form-urlencoded",
)
def test_customer_subscription_created_no_nickname(self) -> None:
expected_topic_name = "cus_00000000000000"
expected_message = """\
[Subscription](https://dashboard.stripe.com/subscriptions/sub_E6STM5w5EX3K28) created
Plan: https://dashboard.stripe.com/plans/plan_E6SQ6RAtmLVtzg
Quantity: 800
Billing method: send invoice"""
self.check_webhook(
"customer_subscription_created_no_nickname",
expected_topic_name,
expected_message,
content_type="application/x-www-form-urlencoded",
)
def test_customer_subscription_deleted(self) -> None:
expected_topic_name = "cus_00000000000000"
expected_message = (
"[Subscription](https://dashboard.stripe.com/subscriptions/sub_00000000000000) deleted"
)
self.check_webhook(
"customer_subscription_deleted",
expected_topic_name,
expected_message,
content_type="application/x-www-form-urlencoded",
)
def test_customer_subscription_updated(self) -> None:
expected_topic_name = "cus_00000000000000"
expected_message = """\
[Subscription](https://dashboard.stripe.com/subscriptions/sub_E6STM5w5EX3K28) updated
* Billing cycle anchor is now <time:2019-11-01T12:00:00+00:00>
* Current period end is now <time:2019-11-01T12:00:00+00:00>
* Current period start is now <time:2018-12-06T05:53:55+00:00>
* Start is now <time:2018-12-06T05:53:55+00:00>
* Status is now trialing
* Trial end is now <time:2019-11-01T12:00:00+00:00>
* Trial start is now <time:2018-12-06T05:53:55+00:00>"""
self.check_webhook(
"customer_subscription_updated",
expected_topic_name,
expected_message,
content_type="application/x-www-form-urlencoded",
)
def test_customer_subscription_trial_will_end(self) -> None:
expected_topic_name = "cus_00000000000000"
expected_message = "[Subscription](https://dashboard.stripe.com/subscriptions/sub_00000000000000) trial will end in 3 days"
# 3 days before the end of the trial, plus a little bit to make sure the rounding is working
with mock.patch("time.time", return_value=1480892861 - 3 * 3600 * 24 + 100):
# use fixture named stripe_customer_subscription_trial_will_end
self.check_webhook(
"customer_subscription_trial_will_end",
expected_topic_name,
expected_message,
content_type="application/x-www-form-urlencoded",
)
def test_customer_updated__account_balance(self) -> None:
expected_topic_name = "cus_00000000000000"
expected_message = (
"[Customer](https://dashboard.stripe.com/customers/cus_00000000000000) updated"
"\n* Account balance is now 100"
)
self.check_webhook(
"customer_updated__account_balance",
expected_topic_name,
expected_message,
content_type="application/x-www-form-urlencoded",
)
def test_customer_discount_created(self) -> None:
expected_topic_name = "cus_00000000000000"
expected_message = "Discount created ([25.5% off](https://dashboard.stripe.com/coupons/25_00000000000000))."
self.check_webhook(
"customer_discount_created",
expected_topic_name,
expected_message,
content_type="application/x-www-form-urlencoded",
)
def test_invoice_payment_failed(self) -> None:
expected_topic_name = "cus_00000000000000"
expected_message = (
"[Invoice](https://dashboard.stripe.com/invoices/in_00000000000000) payment failed"
)
self.check_webhook(
"invoice_payment_failed",
expected_topic_name,
expected_message,
content_type="application/x-www-form-urlencoded",
)
def test_invoice_created(self) -> None:
expected_topic_name = "cus_HH97asvHvaYQYp"
expected_message = """
[Invoice](https://dashboard.stripe.com/invoices/in_1GpmuuHLwdCOCoR7ghzQDQLW) created (manual)
Total: 0.00 INR
Amount due: 0.00 INR
""".strip()
self.check_webhook("invoice_created", expected_topic_name, expected_message)
def test_invoiceitem_created(self) -> None:
expected_topic_name = "cus_00000000000000"
expected_message = "[Invoice item](https://dashboard.stripe.com/invoiceitems/ii_00000000000000) created for 10.00 CAD"
self.check_webhook(
"invoiceitem_created",
expected_topic_name,
expected_message,
content_type="application/x-www-form-urlencoded",
)
def test_invoice_paid(self) -> None:
expected_topic_name = "cus_FDmrSwQt9Fck5M"
expected_message = "[Invoice](https://dashboard.stripe.com/invoices/in_1EjLINHuGUuNWDDZjDf2WNqd) is now paid"
self.check_webhook(
"invoice_updated__paid",
expected_topic_name,
expected_message,
content_type="application/x-www-form-urlencoded",
)
def test_refund_event(self) -> None:
expected_topic_name = "refunds"
expected_message = "A [refund](https://dashboard.stripe.com/refunds/re_1Gib6ZHLwdCOCoR7VrzCnXlj) for a [charge](https://dashboard.stripe.com/charges/ch_1Gib61HLwdCOCoR71rnkccye) of 300000.00 INR was updated."
self.check_webhook("refund_event", expected_topic_name, expected_message)
def test_pseudo_refund_event(self) -> None:
expected_topic_name = "refunds"
expected_message = "A [refund](https://dashboard.stripe.com/refunds/pyr_abcde12345ABCDF) for a [payment](https://dashboard.stripe.com/payments/py_abcde12345ABCDG) of 12.34 EUR was updated."
self.check_webhook("pseudo_refund_event", expected_topic_name, expected_message)
@patch("zerver.webhooks.stripe.view.check_send_webhook_message")
def test_account_updated_without_previous_attributes_ignore(
self, check_send_webhook_message_mock: MagicMock
) -> None:
self.url = self.build_webhook_url()
payload = self.get_body("account_updated_without_previous_attributes")
result = self.client_post(self.url, payload, content_type="application/json")
self.assertFalse(check_send_webhook_message_mock.called)
self.assert_json_success(result)

View File

@@ -0,0 +1,385 @@
# Webhooks for external integrations.
import time
from collections.abc import Sequence
from dataclasses import dataclass
from django.http import HttpRequest, HttpResponse
from zerver.decorator import webhook_view
from zerver.lib.exceptions import UnsupportedWebhookEventTypeError
from zerver.lib.response import json_success
from zerver.lib.timestamp import datetime_to_global_time, timestamp_to_datetime
from zerver.lib.typed_endpoint import JsonBodyPayload, typed_endpoint
from zerver.lib.validator import WildValue, check_bool, check_int, check_none_or, check_string
from zerver.lib.webhooks.common import check_send_webhook_message
from zerver.models import UserProfile
class SuppressedEventError(Exception):
pass
class NotImplementedEventTypeError(SuppressedEventError):
pass
ALL_EVENT_TYPES = [
"charge.dispute.closed",
"charge.dispute.created",
"charge.failed",
"charge.succeeded",
"customer.created",
"customer.deleted",
"customer.discount.created",
"customer.subscription.created",
"customer.subscription.deleted",
"customer.subscription.trial_will_end",
"customer.subscription.updated",
"customer.updated",
"invoice.created",
"invoice.updated",
"invoice.payment_failed",
"invoiceitem.created",
"charge.refund.updated",
]
@webhook_view("Stripe", all_event_types=ALL_EVENT_TYPES)
@typed_endpoint
def api_stripe_webhook(
request: HttpRequest,
user_profile: UserProfile,
*,
payload: JsonBodyPayload[WildValue],
stream: str = "test",
) -> HttpResponse:
try:
topic_name, body = topic_and_body(payload)
except SuppressedEventError: # nocoverage
return json_success(request)
check_send_webhook_message(
request, user_profile, topic_name, body, payload["type"].tame(check_string)
)
return json_success(request)
def topic_and_body(payload: WildValue) -> tuple[str, str]:
event_type = payload["type"].tame(
check_string
) # invoice.created, customer.subscription.created, etc
if len(event_type.split(".")) == 3:
category, resource, event = event_type.split(".")
else:
resource, event = event_type.split(".")
category = resource
object_ = payload["data"]["object"] # The full, updated Stripe object
# Set the topic to the customer_id when we can
topic_name = ""
customer_id = object_.get("customer").tame(check_none_or(check_string))
if customer_id is not None:
# Running into the 60 character topic limit.
# topic = '[{}](https://dashboard.stripe.com/customers/{})' % (customer_id, customer_id)
topic_name = customer_id
body = None
def charge_object_type(charge_id: str) -> str:
# Legacy ACH-style payments report object_type "charge" but use a "py_" id prefix.
return "payment" if charge_id.startswith("py_") else "charge"
def update_string(blacklist: Sequence[str] = []) -> str:
assert "previous_attributes" in payload["data"]
previous_attributes = set(payload["data"]["previous_attributes"].keys()).difference(
blacklist
)
if not previous_attributes: # nocoverage
raise SuppressedEventError
return "".join(
"\n* "
+ attribute.replace("_", " ").capitalize()
+ " is now "
+ stringify(object_[attribute].value)
for attribute in sorted(previous_attributes)
)
def default_body(update_blacklist: Sequence[str] = []) -> str:
id = object_["id"].tame(check_string)
type = object_["object"].tame(check_string)
body = "{resource} {verbed}".format(
resource=linkified_id(id, type),
verbed=event.replace("_", " "),
)
if event == "updated":
return body + update_string(blacklist=update_blacklist)
return body
if category == "account": # nocoverage
if resource == "account":
if event == "updated":
if "previous_attributes" not in payload["data"]:
raise SuppressedEventError
topic_name = "account updates"
body = update_string()
else:
# Part of Stripe Connect
raise NotImplementedEventTypeError
if category == "application_fee": # nocoverage
# Part of Stripe Connect
raise NotImplementedEventTypeError
if category == "balance": # nocoverage
# Not that interesting to most businesses, I think
raise NotImplementedEventTypeError
if category == "charge":
if resource == "charge":
if not topic_name: # only in legacy fixtures
topic_name = "charges"
object_id = object_["id"].tame(check_string)
charge_type = charge_object_type(object_id)
body = "{resource} for {amount} {verbed}".format(
resource=linkified_id(object_id, charge_type),
amount=amount_string(
object_["amount"].tame(check_int), object_["currency"].tame(check_string)
),
verbed=event,
)
if object_["failure_code"]: # nocoverage
body += ". Failure code: {}".format(object_["failure_code"].tame(check_string))
if resource == "dispute":
topic_name = "disputes"
body = default_body() + ". Current status: {status}.".format(
status=object_["status"].tame(check_string).replace("_", " ")
)
if resource == "refund":
topic_name = "refunds"
id = object_["id"].tame(check_string)
type = object_["object"].tame(check_string)
charge_id = object_["charge"].tame(check_string)
charge_type = charge_object_type(charge_id)
body = "A {resource} for a {charge} of {amount} was updated.".format(
resource=linkified_id(id, type, lower=True),
charge=linkified_id(charge_id, charge_type, lower=True),
amount=amount_string(
object_["amount"].tame(check_int), object_["currency"].tame(check_string)
),
)
if category == "checkout_beta": # nocoverage
# Not sure what this is
raise NotImplementedEventTypeError
if category == "coupon": # nocoverage
# Not something that likely happens programmatically
raise NotImplementedEventTypeError
if category == "customer":
if resource == "customer":
# Running into the 60 character topic limit.
# topic = '[{}](https://dashboard.stripe.com/customers/{})' % (object_['id'], object_['id'])
topic_name = object_["id"].tame(check_string)
body = default_body(update_blacklist=["delinquent", "currency", "default_source"])
if event == "created":
if object_["email"]:
body += "\nEmail: {}".format(object_["email"].tame(check_string))
if object_["metadata"]: # nocoverage
for key, value in object_["metadata"].items():
body += f"\n{key}: {value.tame(check_string)}"
if resource == "discount":
body = "Discount {verbed} ([{coupon_name}]({coupon_url})).".format(
verbed=event.replace("_", " "),
coupon_name=object_["coupon"]["name"].tame(check_string),
coupon_url="https://dashboard.stripe.com/{}/{}".format(
"coupons", object_["coupon"]["id"].tame(check_string)
),
)
if resource == "source": # nocoverage
body = default_body()
if resource == "subscription":
body = default_body()
if event == "trial_will_end":
DAY = 60 * 60 * 24 # seconds in a day
# Basically always three: https://stripe.com/docs/api/python#event_types
body += " in {days} days".format(
days=int((object_["trial_end"].tame(check_int) - time.time() + DAY // 2) // DAY)
)
if event == "created":
if object_["plan"]:
nickname = object_["plan"]["nickname"].tame(check_none_or(check_string))
if nickname is not None:
body += "\nPlan: [{plan_nickname}](https://dashboard.stripe.com/plans/{plan_id})".format(
plan_nickname=object_["plan"]["nickname"].tame(check_string),
plan_id=object_["plan"]["id"].tame(check_string),
)
else:
body += "\nPlan: https://dashboard.stripe.com/plans/{plan_id}".format(
plan_id=object_["plan"]["id"].tame(check_string),
)
if object_["quantity"]:
body += "\nQuantity: {}".format(object_["quantity"].tame(check_int))
if "billing" in object_: # nocoverage
body += "\nBilling method: {}".format(
object_["billing"].tame(check_string).replace("_", " ")
)
if category == "file": # nocoverage
topic_name = "files"
body = default_body() + " ({purpose}). \nTitle: {title}".format(
purpose=object_["purpose"].tame(check_string).replace("_", " "),
title=object_["title"].tame(check_string),
)
if category == "invoice":
if event == "upcoming": # nocoverage
body = "Upcoming invoice created"
elif (
event == "updated"
and payload["data"]["previous_attributes"].get("paid").tame(check_none_or(check_bool))
is False
and object_["paid"].tame(check_bool) is True
and object_["amount_paid"].tame(check_int) != 0
and object_["amount_remaining"].tame(check_int) == 0
):
# We are taking advantage of logical AND short circuiting here since we need the else
# statement below.
object_id = object_["id"].tame(check_string)
invoice_link = f"https://dashboard.stripe.com/invoices/{object_id}"
body = f"[Invoice]({invoice_link}) is now paid"
else:
body = default_body(
update_blacklist=[
"lines",
"description",
"number",
"finalized_at",
"status_transitions",
"payment_intent",
]
)
if event == "created":
# Could potentially add link to invoice PDF here
body += " ({reason})\nTotal: {total}\nAmount due: {due}".format(
reason=object_["billing_reason"].tame(check_string).replace("_", " "),
total=amount_string(
object_["total"].tame(check_int), object_["currency"].tame(check_string)
),
due=amount_string(
object_["amount_due"].tame(check_int), object_["currency"].tame(check_string)
),
)
if category == "invoiceitem":
body = default_body(update_blacklist=["description", "invoice"])
if event == "created":
body += " for {amount}".format(
amount=amount_string(
object_["amount"].tame(check_int), object_["currency"].tame(check_string)
)
)
if category.startswith("issuing"): # nocoverage
# Not implemented
raise NotImplementedEventTypeError
if category.startswith("order"): # nocoverage
# Not implemented
raise NotImplementedEventTypeError
if category in [
"payment_intent",
"payout",
"plan",
"product",
"recipient",
"reporting",
"review",
"sigma",
"sku",
"source",
"subscription_schedule",
"topup",
"transfer",
]: # nocoverage
# Not implemented. In theory doing something like
# body = default_body()
# may not be hard for some of these
raise NotImplementedEventTypeError
if body is None:
raise UnsupportedWebhookEventTypeError(event_type)
return (topic_name, body)
def amount_string(amount: int, currency: str) -> str:
zero_decimal_currencies = [
"bif",
"djf",
"jpy",
"krw",
"pyg",
"vnd",
"xaf",
"xpf",
"clp",
"gnf",
"kmf",
"mga",
"rwf",
"vuv",
"xof",
]
if currency in zero_decimal_currencies:
decimal_amount = str(amount) # nocoverage
else:
decimal_amount = f"{float(amount) * 0.01:.02f}"
if currency == "usd": # nocoverage
return "$" + decimal_amount
return decimal_amount + f" {currency.upper()}"
@dataclass
class DisplayInfo:
name: str
url_prefix: str | None = None
STRIPE_OBJECT_TYPES: dict[str, DisplayInfo] = {
# CORE RESOURCES
"charge": DisplayInfo("Charge", "charges"),
"customer": DisplayInfo("Customer", "customers"),
"dispute": DisplayInfo("Dispute", "disputes"),
"file": DisplayInfo("File", "files"),
"file_link": DisplayInfo("File link", "file_links"),
"payment_intent": DisplayInfo("Payment intent", "payment_intents"),
"payout": DisplayInfo("Payout", "payouts"),
"product": DisplayInfo("Product", "products"),
"refund": DisplayInfo("Refund", "refunds"),
"token": DisplayInfo("Token", "tokens"),
# PAYMENT METHODS
# Payment methods have URL prefixes like /customers/cus_id/sources.
"bank_account": DisplayInfo("Bank account"),
"card": DisplayInfo("Card"),
"source": DisplayInfo("Source"),
# BILLING
# Coupons have a configurable id, but the URL prefix is /coupons.
# It's not clear if discounts have a URL.
"invoice": DisplayInfo("Invoice", "invoices"),
"invoiceitem": DisplayInfo("Invoice item", "invoiceitems"),
# Products are covered in core resources.
# Plans have a configurable id, though by default they are created with this pattern:
# 'plan': ('Plan', 'plans').
"subscription": DisplayInfo("Subscription", "subscriptions"),
"subscription_item": DisplayInfo("Subscription item", "subscription_items"),
# Usage records seem to have URL prefixes like /subscription_items/si_id/usage_record_summaries.
"usage_record": DisplayInfo("Usage record"),
# UNDOCUMENTED
"payment": DisplayInfo("Payment", "payments"),
# Connect, Fraud, Orders, etc not implemented.
}
def linkified_id(id: str, type: str, lower: bool = False) -> str:
display_info = STRIPE_OBJECT_TYPES[type]
name = display_info.name
if lower:
name = name.lower()
if display_info.url_prefix is None: # nocoverage
return name
return f"[{name}](https://dashboard.stripe.com/{display_info.url_prefix}/{id})"
def stringify(value: object) -> str:
if isinstance(value, int) and value > 1500000000 and value < 2000000000:
return datetime_to_global_time(timestamp_to_datetime(value))
return str(value)