diff --git a/make_post_sell/scripts/alembic/versions/7d6af811b6a1_bump_existing_shops_offer_expiration_.py b/make_post_sell/scripts/alembic/versions/7d6af811b6a1_bump_existing_shops_offer_expiration_.py new file mode 100644 index 0000000..392bb2c --- /dev/null +++ b/make_post_sell/scripts/alembic/versions/7d6af811b6a1_bump_existing_shops_offer_expiration_.py @@ -0,0 +1,37 @@ +"""bump existing shops offer_expiration_hours 168 to 72 + +Revision ID: 7d6af811b6a1 +Revises: 5d01b163b805 +Create Date: 2026-05-14 20:42:51.956494 + +Default offer expiration (pre-acceptance window) dropped from 168h +(7 days) → 48h → 72h across recent commits. Existing shops kept +whatever value they had at table-create time. This migration bumps +any shop still at the literal old default 168 to the new default +72. Shops that explicitly customized via the offer-settings form +(any value other than 168) are left alone. +""" +from alembic import op +import sqlalchemy as sa + + +revision = "7d6af811b6a1" +down_revision = "5d01b163b805" +branch_labels = None +depends_on = None + + +def upgrade(): + op.execute( + sa.text( + "UPDATE mps_shop " + "SET offer_expiration_hours = 72 " + "WHERE offer_expiration_hours = 168" + ) + ) + + +def downgrade(): + # No safe reversal — we don't know which shops were at 168 by + # default vs explicit. Leave them at 72 on rollback. + pass diff --git a/make_post_sell/views/cart.py b/make_post_sell/views/cart.py index 7eaf968..99115ca 100644 --- a/make_post_sell/views/cart.py +++ b/make_post_sell/views/cart.py @@ -21,6 +21,8 @@ from ..lib.mail import ( ) from ..lib.notifications import notify_purchase_and_sale +import logging + import stripe import traceback from datetime import datetime @@ -895,13 +897,24 @@ def cart_complete_checkout(request): return HTTPFound(redirect_url) except stripe.error.CardError as e: - request.tm.abort() + # doom() — not abort() — so pyramid_tm.tm_tween still owns + # the txn lifecycle. abort() removes the txn outright, then + # tm_tween's post-view manager.isDoomed() check raises + # NoTransaction → uwsgi 500 → Caddy 502. doom() leaves it + # for tm_tween to clean up. + request.tm.doom() + logging.getLogger(__name__).exception( + "stripe checkout failed (user-visible)" + ) msg = ("Payment failed. Please check your card details.", "error") request.session.flash(msg) return HTTPFound("/billing") except Exception as e: - request.tm.abort() + request.tm.doom() + logging.getLogger(__name__).exception( + "stripe checkout unexpected failure" + ) msg = (f"Payment failed: {str(e)}", "error") request.session.flash(msg) return HTTPFound("/billing") @@ -1117,7 +1130,16 @@ def paypal_complete_checkout(request): return HTTPFound("/cart") except Exception as e: - request.tm.abort() + # doom() not abort() — see Stripe paths above for the + # pyramid_tm rationale (502 root cause on 2026-05-14). + # The original tm.abort() yanked the txn out from under + # pyramid_tm.tm_tween, whose post-view manager.isDoomed() + # check then raised NoTransaction → uwsgi 500 → Caddy 502. + # User got charged via PayPal/Adyen but no invoice landed. + request.tm.doom() + logging.getLogger(__name__).exception( + "payment complete-checkout unexpected failure" + ) request.session.flash((f"Payment processing failed: {str(e)}", "error")) return HTTPFound("/cart") @@ -1359,7 +1381,16 @@ def adyen_complete_checkout(request): return HTTPFound("/cart") except Exception as e: - request.tm.abort() + # doom() not abort() — see Stripe paths above for the + # pyramid_tm rationale (502 root cause on 2026-05-14). + # The original tm.abort() yanked the txn out from under + # pyramid_tm.tm_tween, whose post-view manager.isDoomed() + # check then raised NoTransaction → uwsgi 500 → Caddy 502. + # User got charged via PayPal/Adyen but no invoice landed. + request.tm.doom() + logging.getLogger(__name__).exception( + "payment complete-checkout unexpected failure" + ) request.session.flash((f"Payment processing failed: {str(e)}", "error")) return HTTPFound("/cart")