Skip to content

Add payment plan switching - #47

Open
rechner wants to merge 6 commits into
devfrom
rechner/add-switch-plan
Open

rechner wants to merge 6 commits into
devfrom
rechner/add-switch-plan

Conversation

@rechner

@rechner rechner commented Sep 19, 2026

Copy link
Copy Markdown
Member

Closes #41, focused on switching to another payment plan in the same tier with the same subscription period. Mostly to enable Sudoroom members existing pay-what-you-can dues model.

Complicated to include a durable recovery path. Should hopefully tell you when something is wrong/unrecoverable.

  • Add PaymentPlanSwitchOperation model to track Stripe subscription plan changes durably
  • Implement plan switching logic
  • Enables idempotent payment plan switches with Stripe sync

- Add "type": "module" to package.json to enable ES module support
- Extract axios, i18n, and Sentry configuration into src/services/api.ts service module
- Lazy-load apex charts library
- Add PaymentPlanSwitchOperation model to track Stripe subscription plan changes durably
- Implement plan switching logic
- Enables idempotent payment plan switches with Stripe sync
@github-actions

Copy link
Copy Markdown

Created image with name ghcr.io/pawprintprototyping/membermatters:untrusted-pr-image-PawprintPrototyping-rechner-add-switch-plan. WARNING: run this image at your own risk - it was created from a potentially untrusted PR.

xsrfCookieName: 'csrftoken',
xsrfHeaderName: 'X-CSRFTOKEN',
});
import { api } from '../services/api';

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These frontend changes are unrelated to fix compiler warnings

'axios',
'routeGuards',
'capacitor',
'apexcharts',

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apexcharts is hulking, reworked to lazy load on the necessary page instead.

- Add tests for stripe helper functions accepting dict and string objects
- Add test for idempotentcy when profile already at target plan
- Add test for profile drift detection
- Test for database failure tolerance in error recording
- Add test for unconfirmed stripe update retry logic
- Add tests for plan switch validation (missing plan ID, locked member, same plan)
- Add tests for pending and failed operation rejection
- Add tests for subscription item count and price validation
- Add tests for terminal recovery failure reporting
- Improve test coverage for payment plan switch recovery and error handling
@github-actions

github-actions Bot commented Sep 19, 2026 •

Copy link
Copy Markdown

Created image with name ghcr.io/pawprintprototyping/membermatters:untrusted-pr-image-PawprintPrototyping-rechner-add-switch-plan. WARNING: run this image at your own risk - it was created from a potentially untrusted PR.

@rechner rechner added the enhancement New feature or request label Sep 25, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems strange and I have no idea what it is doing. Probably some django thing I am not familiar with.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything in the migrations folder is a Django ORM database migration. After you change the ORM model, you run ./manage.py makemigration and the corresponding database changes are automatically generated.



def _stripe_value(resource, name, default=None):
if isinstance(resource, dict):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really really strange. Did stripe like stop using dicts at some point and we are trying to stay compatible?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v15 of the Stripe SDK changed the signature, yeah. There's no reason to go back, I could remove this crutch but wasn't sure originally if it was an SDK change or an API change.


def _price_id(item):
price = _stripe_value(item, "price")
if isinstance(price, str):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These isinsntance checks make me question the stripe API and what kind of objects it hands back to you



def _record_operation_error(operation_id, error):
message = str(error)[:4000]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a huge fan of this hardcoded 4000 character limit here. I am guessing this is to keep it from overloading a database entry or something. If this is a hard upper limit, maybe it should defined as a constant somewhere.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Theoretically this field has no limit, but any message getting passed here longer than 4k is likely a bug.

return Response({"success": False})


class PaymentPlanSwitch(StripeAPIView):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same concern with the isinstance and getattr based helpers in this class. The stripe API either sucks or we are doing something weird.

@kaybarkbark kaybarkbark left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am genuinely curious why we need to check the types of whatever the stripe API is returning to us and either handle it as a dict or an object. Maybe that's a genuine hack for some horrible thing Stripe did. Regardless, the python has been reviewed. I have not reviewed the typescript.

@sonarqubecloud

sonarqubecloud Bot commented Oct 1, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add feature to switch membership tiers

2 participants