Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ releases, in reverse chronological order.
v4.0.0
------

- Fixed ``StripeProviderV3`` not setting ``captured_amount`` on payment
confirmation in ``process_data()`` and ``status()``, which broke refunds.
- ``StripeProvider``, which was deprecated in v3.0.0, has been dropped. Use
``StripeProviderV3`` instead.
- Drop support for Django 5.0.
Expand Down
2 changes: 2 additions & 0 deletions payments/stripe/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ def status(self, payment):
stripe.api_key = self.api_key
session = stripe.checkout.Session.retrieve(payment.transaction_id)
if session.payment_status == "paid":
payment.captured_amount = payment.total
payment.change_status(PaymentStatus.CONFIRMED)
payment.attrs.session = session
payment.save()
Expand Down Expand Up @@ -257,6 +258,7 @@ def process_data(self, payment, request):

elif session_info["payment_status"] == "paid":
# Paid Order
payment.captured_amount = payment.total
payment.change_status(PaymentStatus.CONFIRMED)

payment.attrs.session = session_info
Expand Down
76 changes: 72 additions & 4 deletions payments/stripe/test_stripev3.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import json
from unittest.mock import Mock
from unittest.mock import patch

Expand Down Expand Up @@ -118,17 +119,36 @@ def test_provider_create_session_success_with_billing_name():
provider.create_session(payment)


@pytest.mark.skip(reason="Breaks global state and asserts nothing")
def test_provider_status():
def test_provider_status_confirmed():
payment = Payment()
payment.attrs = payment_attrs()
payment.transaction_id = "cs_test_..."
provider = StripeProviderV3(api_key=API_KEY)

class return_value:
class MockSession:
payment_status = "paid"

with patch("stripe.checkout.Session.retrieve", return_value=return_value):
with patch("stripe.checkout.Session.retrieve", return_value=MockSession()):
provider.status(payment)

assert payment.status == PaymentStatus.CONFIRMED
assert payment.captured_amount == payment.total


def test_provider_status_not_paid():
payment = Payment()
payment.transaction_id = "cs_test_..."
provider = StripeProviderV3(api_key=API_KEY)

class MockSession:
payment_status = "unpaid"

with patch("stripe.checkout.Session.retrieve", return_value=MockSession()):
provider.status(payment)

assert payment.status == PaymentStatus.WAITING
assert payment.captured_amount == 0


def test_provider_refund_failure_bad_status():
payment = Payment()
Expand Down Expand Up @@ -172,3 +192,51 @@ def test_provider_refund_success():
provider.refund(payment)

assert payment.status == PaymentStatus.REFUNDED


def _make_webhook_request(event_type, session_status, payment_status):
body = json.dumps(
{
"type": event_type,
"data": {
"object": {
"status": session_status,
"payment_status": payment_status,
"payment_intent": "pi_...",
}
},
}
)
request = Mock()
request.body = body
return request


def test_process_data_sets_captured_amount_on_payment():
payment = Payment()
provider = StripeProviderV3(api_key=API_KEY, secure_endpoint=False)
request = _make_webhook_request(
event_type="checkout.session.completed",
session_status="complete",
payment_status="paid",
)

provider.process_data(payment, request)

assert payment.status == PaymentStatus.CONFIRMED
assert payment.captured_amount == payment.total


def test_process_data_does_not_set_captured_amount_on_expiry():
payment = Payment()
provider = StripeProviderV3(api_key=API_KEY, secure_endpoint=False)
request = _make_webhook_request(
event_type="checkout.session.expired",
session_status="expired",
payment_status="unpaid",
)

provider.process_data(payment, request)

assert payment.status == PaymentStatus.REJECTED
assert payment.captured_amount == 0
Loading