integrations/stripe: Fix bug in charge.succeeded.

There are (at least) two types of objects that could be sent with a
charge.succeeded event, a Charge (e.g. for credit cards) or a Payment (if
they pay by ACH). We were handling the first but not the second.

This commit also updates the fixture for the existing charge.succeeded event
to the latest API version.
This commit is contained in:
Rishi Gupta
2018-12-02 00:21:09 -08:00
parent 5696ad5490
commit 36f54e689b
5 changed files with 222 additions and 81 deletions

View File

@@ -1,76 +0,0 @@
{
"created": 1326853478,
"livemode": false,
"id": "evt_00000000000000",
"type": "charge.succeeded",
"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": 1480671988,
"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": true,
"receipt_email": null,
"receipt_number": null,
"refunded": false,
"refunds": {
"object": "list",
"data": [],
"has_more": false,
"total_count": 0,
"url": "/v1/charges/ch_19MDceCV4wXizEw45TBCADlp/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

@@ -27,10 +27,18 @@ class StripeHookTests(WebhookTestCase):
self.send_and_test_stream_message('charge_failed', expected_topic, expected_message,
content_type="application/x-www-form-urlencoded")
def test_charge_succeeded(self) -> None:
expected_topic = u"charges"
expected_message = u"[Charge](https://dashboard.stripe.com/charges/ch_00000000000000) for 1.00 AUD succeeded"
self.send_and_test_stream_message('charge_succeeded', expected_topic, expected_message,
# Credit card charge
def test_charge_succeeded__card(self) -> None:
expected_topic = u"cus_00000000000000"
expected_message = u"[Charge](https://dashboard.stripe.com/charges/ch_000000000000000000000000) for 1.00 AUD succeeded"
self.send_and_test_stream_message('charge_succeeded__card', expected_topic, 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 = u"cus_00000000000000"
expected_message = u"[Payment](https://dashboard.stripe.com/payments/py_000000000000000000000000) for $1.00 succeeded"
self.send_and_test_stream_message('charge_succeeded__invoice', expected_topic, expected_message,
content_type="application/x-www-form-urlencoded")
def test_customer_created(self) -> None:

View File

@@ -55,7 +55,7 @@ def api_stripe_webhook(request: HttpRequest, user_profile: UserProfile,
return json_success()
if category == 'charge':
if resource == 'charge':
if not topic:
if not topic: # only in legacy fixtures
topic = 'charges'
body = "{resource} for {amount} {verbed}".format(
resource=linkified_id(object_['id']),
@@ -208,6 +208,9 @@ def linkified_id(object_id: str, lower: bool=False) -> str:
# I think usage records have URL prefixes like /subscription_items/si_id/usage_record_summaries
'mbur': ('Usage record', None),
# Undocumented :|
'py': ('Payment', 'payments'),
# Connect, Fraud, Orders, etc not implemented
} # type: Dict[str, Tuple[str, Optional[str]]]
name, url_prefix = names_and_urls[object_id.split('_')[0]]