Skip to content

Commit

Permalink
server: Raise RefundedAlready vs. Stripe 500
Browse files Browse the repository at this point in the history
  • Loading branch information
birkjernstrom committed Jan 23, 2025
1 parent 0fa5e65 commit 712b62d
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 12 deletions.
33 changes: 21 additions & 12 deletions server/polar/refund/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,19 +182,28 @@ async def create(
raise RefundUnknownPayment(order.id, payment_type="order")

refund_total = refund_amount + refund_tax_amount
stripe_refund = await stripe_service.create_refund(
charge_id=payment.charge_id,
amount=refund_total,
reason=RefundReason.to_stripe(create_schema.reason),
metadata=dict(
order_id=str(order.id),
charge_id=str(payment.charge_id),
amount=str(create_schema.amount),
refund_amount=str(refund_amount),
refund_tax_amount=str(refund_tax_amount),
revoke_benefits="1" if create_schema.revoke_benefits else "0",
),
stripe_metadata = dict(
order_id=str(order.id),
charge_id=str(payment.charge_id),
amount=str(create_schema.amount),
refund_amount=str(refund_amount),
refund_tax_amount=str(refund_tax_amount),
revoke_benefits="1" if create_schema.revoke_benefits else "0",
)

try:
stripe_refund = await stripe_service.create_refund(
charge_id=payment.charge_id,
amount=refund_total,
reason=RefundReason.to_stripe(create_schema.reason),
metadata=stripe_metadata,
)
except stripe_lib.InvalidRequestError as e:
if e.code == "charge_already_refunded":
raise RefundedAlready(order)
else:
raise e

internal_create_schema = self.build_create_schema_from_stripe(
stripe_refund,
order=order,
Expand Down
53 changes: 53 additions & 0 deletions server/tests/refunds/test_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from unittest.mock import MagicMock

import pytest
import stripe as stripe_lib
from httpx import AsyncClient, Response
from pytest_mock import MockerFixture

Expand All @@ -25,6 +26,7 @@
from polar.pledge.service import pledge as pledge_service
from polar.postgres import AsyncSession
from polar.refund.schemas import RefundCreate
from polar.refund.service import RefundedAlready
from polar.refund.service import refund as refund_service
from polar.transaction.service.refund import (
refund_transaction as refund_transaction_service,
Expand Down Expand Up @@ -221,6 +223,57 @@ async def assert_transaction_amounts_from_refund(
return refund_transaction


@pytest.mark.asyncio
class TestCreateAbuse(StripeRefund):
async def test_create_repeatedly(
self,
session: AsyncSession,
mocker: MockerFixture,
stripe_service_mock: MagicMock,
save_fixture: SaveFixture,
product: Product,
refund_hooks: Hooks,
customer: Customer,
) -> None:
# Complex Swedish order. $99.9 with 25% VAT = $24.75
order, payment = await create_order_and_payment(
save_fixture,
product=product,
customer=customer,
amount=1000,
tax_amount=250,
)

assert order.refunded_amount == 0
assert order.refunded_tax_amount == 0

stripe_error = stripe_lib.InvalidRequestError(
"Charge py_XX has already been refunded.",
param=None,
code="charge_already_refunded",
)
stripe_service_mock.create_refund.side_effect = stripe_error

# Raised by us or Stripe, e.g attempting a POST request in a quick loop
with pytest.raises(RefundedAlready):
await refund_service.create(
session,
order,
RefundCreate(
order_id=order.id,
reason=RefundReason.service_disruption,
amount=1000,
comment=None,
revoke_benefits=False,
),
)

order = await order_service.get(session, order.id) # type: ignore
assert order
assert order.refunded_amount == 0
assert order.refunded_tax_amount == 0


@pytest.mark.asyncio
class TestCreatedWebhooks(StripeRefund):
async def test_valid_pledge_refund(
Expand Down

0 comments on commit 712b62d

Please sign in to comment.