diff --git a/Changelog.md b/Changelog.md index 3042de4b1a1e44397841e642d2a056b1df463a82..f0d2343624349deb3c8e8a882e0f1a5ce0ee3b8f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. +## [0.16] - 2025-02-12 +- Exclude void invoices from possbile error report +- Limit max invoice due date and set maximum date for allowing bank transfers + +... + ## [0.9] - 2025-02-12 - added repeated confirm click prevention - added emails sent out when a payment is received with no associated order diff --git a/config-example.json b/config-example.json index 962a994f513251c346de38828238ce0ef96f3a1a..43e4b0d6d57a79828be2a28a8ce5f5f71ceb59de 100644 --- a/config-example.json +++ b/config-example.json @@ -5,5 +5,7 @@ "STRIPE_TAX_RATE_ID": "txr_1QeddlDSpyjzuj5pPwUcMwTd", "VISIT_API_KEY": "<visit api key>", "VISIT_EXPO_ID": "18lm2fafttito", - "SEND_ERROR_EMAILS_TO": [] + "SEND_ERROR_EMAILS_TO": [], + "MAX_INVOICE_DUE_DATE": "2025-05-23", + "MAX_BANK_TRANSFER_ALLOWED_DATE": "2025-05-12" } \ No newline at end of file diff --git a/setup.py b/setup.py index b99e69646a587d86bc5cebe4622432d11e6fd15f..42f1c8e39df35575aec35bfb5f9c8436c0802b53 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="stripe-checkout", - version="0.15", + version="0.16", author="GEANT", author_email="swd@geant.org", description="Stripe custom checkout support service", diff --git a/stripe_checkout/stripe_checkout/compare_visit_stripe.py b/stripe_checkout/stripe_checkout/compare_visit_stripe.py index a6a0648e00a4d8e526a6cc552ab2023721a557c0..41163bb82c49097f4252721c246d80ee5e80085d 100644 --- a/stripe_checkout/stripe_checkout/compare_visit_stripe.py +++ b/stripe_checkout/stripe_checkout/compare_visit_stripe.py @@ -43,6 +43,8 @@ class PossibleErrorReporter(CSVReporter): yield obj unseen_invoices: dict[str, list[dict]] = {} for obj_id, obj in data["stripe"].items(): + if obj["status"] in ("draft", "void"): + continue email = obj["customer_email"] if obj_id.startswith("in_") and email not in seen: unseen_invoices.setdefault(email, []).append(obj) @@ -105,6 +107,13 @@ reporter = PossibleErrorReporter([VISITOR_SERIALIZER], get_data=get_all_data) def main(): import logging import pathlib + import stripe + + from django.conf import settings + from stripe_checkout.config import load_config + + load_config("config.json", settings=settings) + stripe.api_key = settings.STRIPE_API_KEY logger = logging.getLogger() logging.basicConfig(level=logging.INFO) diff --git a/stripe_checkout/stripe_checkout/stripe_.py b/stripe_checkout/stripe_checkout/stripe_.py index 4b96623059d16fcfd4b780fe4ac64021468dfccf..38163e355f0a66bbb2f5d588ae6b3cfaea5e7d1b 100644 --- a/stripe_checkout/stripe_checkout/stripe_.py +++ b/stripe_checkout/stripe_checkout/stripe_.py @@ -1,5 +1,6 @@ from __future__ import annotations +import datetime import functools import logging import time @@ -72,6 +73,33 @@ def parse_address(visit_address: dict, organization: str): } +def get_invoice_days_until_due(max_days=30): + max_invoice_due_date = getattr(settings, "MAX_INVOICE_DUE_DATE", None) + if not max_invoice_due_date: + return max_days + days_until_max_due_date = ( + datetime.date.fromisoformat(max_invoice_due_date) - datetime.date.today() + ).days + return max(min(days_until_max_due_date, max_days), 0) + + +def _is_bank_transfer_allowed(): + max_bank_transfer_allowed_date = getattr( + settings, "MAX_BANK_TRANSFER_ALLOWED_DATE", None + ) + if not max_bank_transfer_allowed_date: + return True + return datetime.date.today() <= datetime.date.fromisoformat( + max_bank_transfer_allowed_date + ) + + +def get_allowed_payment_methods(): + if _is_bank_transfer_allowed(): + return ["card", "customer_balance"] + return ["card"] + + def create_invoice( shopping_cart: ShoppingCart, customer_id, @@ -98,8 +126,8 @@ def create_invoice( invoice = stripe.Invoice.create( customer=customer_id, collection_method="send_invoice", - payment_settings={"payment_method_types": ["card", "customer_balance"]}, - days_until_due=30, + payment_settings={"payment_method_types": get_allowed_payment_methods()}, + days_until_due=get_invoice_days_until_due(), custom_fields=custom_fields, rendering={"template": settings.STRIPE_INVOICE_TEMPLATE_ID}, metadata=metadata, diff --git a/test/test_stripe.py b/test/test_stripe.py index ed91385a1a028b25374c9076cc53189b54b4e274..ac82543ff254d28e434d9c1495e201850bc5db1d 100644 --- a/test/test_stripe.py +++ b/test/test_stripe.py @@ -1,6 +1,11 @@ +import datetime import pytest import stripe -from stripe_checkout.stripe_checkout.stripe_ import get_or_create_customer +from stripe_checkout.stripe_checkout.stripe_ import ( + get_allowed_payment_methods, + get_invoice_days_until_due, + get_or_create_customer, +) @pytest.mark.parametrize( @@ -95,3 +100,44 @@ def test_updates_visitor_with_address(default_visitor, mock_stripe): new_address = stripe.Customer.modify.call_args.kwargs["address"] non_empty = {key: val for key, val in new_address.items() if val} assert non_empty == {"country": "BE"} + + +@pytest.mark.parametrize( + "max_due_date_in_days, expected_days_until_due", + [ + (None, 30), + (31, 30), + (30, 30), + (29, 29), + (1, 1), + (0, 0), + (-1, 0), + ], +) +def test_invoice_due_date(max_due_date_in_days, expected_days_until_due, settings): + if max_due_date_in_days is not None: + max_due_date = datetime.date.today() + datetime.timedelta( + days=max_due_date_in_days + ) + settings.MAX_INVOICE_DUE_DATE = max_due_date.isoformat() + assert get_invoice_days_until_due() == expected_days_until_due + + +@pytest.mark.parametrize( + "days_until_max_bank_transfer_date, expected_payment_methods", + [ + (None, ["card", "customer_balance"]), + (1, ["card", "customer_balance"]), + (0, ["card", "customer_balance"]), + (-1, ["card"]), + ], +) +def test_get_allowed_payment_methods( + days_until_max_bank_transfer_date, expected_payment_methods, settings +): + if days_until_max_bank_transfer_date is not None: + max_bank_transfer_date = datetime.date.today() + datetime.timedelta( + days=days_until_max_bank_transfer_date + ) + settings.MAX_BANK_TRANSFER_ALLOWED_DATE = max_bank_transfer_date.isoformat() + assert get_allowed_payment_methods() == expected_payment_methods