Skip to content

feat: add PaymentController v2 architecture#192

Open
0spinboson wants to merge 1 commit into
frappe:developfrom
nlvegan:refactor/payment-controller-architecture
Open

feat: add PaymentController v2 architecture#192
0spinboson wants to merge 1 commit into
frappe:developfrom
nlvegan:refactor/payment-controller-architecture

Conversation

@0spinboson
Copy link
Copy Markdown

@0spinboson 0spinboson commented Jan 11, 2026

Summary

Adds a PaymentController base class that standardizes payment gateway integrations using the template method pattern. Based on the architecture from PR #53 by @blaggacao.

The first gateway implementation (Stripe PaymentIntent) is on the feat/stripe-payment-intent branch, stacked on this one.

What's included

  • PaymentController — abstract base class orchestrating initiate → proceed → process_response
  • Payment Session Log — DocType for tracking payment state with document locking and terminal state checks
  • Payment Button — DocType for gateway selection UI
  • /pay — gateway-agnostic payment endpoint
  • payments/types.py — data contracts: TxData, Initiated, Proceeded, Processed, SessionStates
  • payments/exceptions.py — structured error hierarchy
  • is_v2_gateway() — utility for consumers to detect v2-compatible gateways
  • TX data filtering whitelist to prevent parameter tampering
  • Fix delete_custom_fields to use composite key lookup
  • Fix get_checkout_url to route through get_payment_gateway_controller
  • Payment Session Log link field on Payment Request

The interface currently supports the charge flow. Subscription, pre-authorization, and mandate flows are planned.

Tests

28 Python tests covering controller lifecycle, PSL state management, PaymentButton properties, and legacy compatibility.

Breaking changes

None. Existing gateway integrations are unaffected.

@MorezMartin
Copy link
Copy Markdown

MorezMartin commented Jan 11, 2026

Seems to be nice work, better than me, tests etc. will test it on next update
PS : thanks 😁

@0spinboson
Copy link
Copy Markdown
Author

Using Stripe with PaymentController (before ERPNext integration)

The ERPNext Payment Request doctype doesn't yet support the new PaymentController architecture. A small PR (~50 lines) is needed there to detect v2 gateways and call PaymentController.initiate(). Until then, you can still use and test this implementation by linking directly to the checkout page.

Direct Usage

Generate payment URLs in this format:

https://your-site.com/stripe_checkout?amount=100&currency=EUR&reference_doctype=Sales%20Invoice&reference_docname=ACC-SINV-2024-00001

Or programmatically:

from frappe.utils import get_url
from urllib.parse import urlencode

params = {
"amount": invoice.grand_total,
"currency": invoice.currency,
"reference_doctype": "Sales Invoice",
"reference_docname": invoice.name,
}
payment_url = get_url("/stripe_checkout?" + urlencode(params))

Security Note

The URL parameters are validated server-side against the reference document. If a reference_doctype and reference_docname are provided, the amount and currency are fetched from that document directly, preventing URL tampering.

Webhooks

Configure your Stripe webhook to point to:
https://your-site.com/api/method/payments.payment_gateways.doctype.stripe_settings.stripe_settings.stripe_webhook

Events: payment_intent.succeeded, payment_intent.payment_failed, payment_intent.canceled

0spinboson added a commit to nlvegan/erpnext that referenced this pull request Jan 11, 2026
Add support for the new PaymentController architecture from frappe/payments.
When a payment gateway implements PaymentController (v2), Payment Request
uses the standardized flow. Existing v1 gateways continue working unchanged.

Changes:
- Add _is_v2_gateway() to detect PaymentController-based gateways
- Add _process_v2_gateway() for v2 payment flow
- Add get_tx_data() for standardized transaction data
- Branch between v1/v2 flows in before_submit()

The v2 flow uses PaymentController.initiate() to create a Payment Session Log
and generates a standard payment URL (/pay?s=<psl_name>) that works with
the payments app's unified checkout experience.

Tested with Stripe gateway - Payment Request submission creates correct
Payment Session Log and payment page loads successfully.

Related: frappe/payments#192
@0spinboson
Copy link
Copy Markdown
Author

0spinboson commented Jan 11, 2026

https://github.com/0spinboson/erpnext/tree/feat/payment-controller-v2-support < here's a companion patch to test the new gateway approach blaggacao came up with for erpnext

@mahsem
Copy link
Copy Markdown

mahsem commented Jan 13, 2026

https://github.com/0spinboson/erpnext/tree/feat/payment-controller-v2-support < here's a companion patch to test the new gateway approach blaggacao came up with for erpnext

@0spinboson will you submit this PR to erpnext

0spinboson added a commit to nlvegan/erpnext that referenced this pull request Jan 13, 2026
Adds support for the new PaymentController interface from frappe/payments.
This enables Payment Request to work with v2 payment gateways (like the
refactored Stripe integration) while maintaining full backward compatibility
with existing v1 gateways.

Changes:
- Add _is_v2_gateway() helper to detect PaymentController-based gateways
- Branch before_submit to use appropriate flow (v2 vs v1)
- Add _process_v2_gateway() method using PaymentController.initiate()
- Add get_tx_data() to prepare standardized transaction data

Related: frappe/payments#192
@0spinboson
Copy link
Copy Markdown
Author

https://github.com/0spinboson/erpnext/tree/feat/payment-controller-v2-support < here's a companion patch to test the new gateway approach blaggacao came up with for erpnext

@0spinboson will you submit this PR to erpnext

frappe/erpnext#51723

0spinboson added a commit to nlvegan/erpnext that referenced this pull request Jan 13, 2026
Adds support for the new PaymentController interface from frappe/payments.
This enables Payment Request to work with v2 payment gateways (like the
refactored Stripe integration) while maintaining full backward compatibility
with existing v1 gateways.

Changes:
- Add _is_v2_gateway() helper to detect PaymentController-based gateways
- Branch before_submit to use appropriate flow (v2 vs v1)
- Add _process_v2_gateway() method using PaymentController.initiate()
- Add get_tx_data() to prepare standardized transaction data

Related: frappe/payments#192
0spinboson added a commit to nlvegan/erpnext that referenced this pull request Jan 13, 2026
Adds support for the new PaymentController interface from frappe/payments.
This enables Payment Request to work with v2 payment gateways (like the
refactored Stripe integration) while maintaining full backward compatibility
with existing v1 gateways.

Changes:
- Add _is_v2_gateway() helper to detect PaymentController-based gateways
- Branch before_submit to use appropriate flow (v2 vs v1)
- Add _process_v2_gateway() method using PaymentController.initiate()
- Add get_tx_data() to prepare standardized transaction data

Related: frappe/payments#192
0spinboson added a commit to nlvegan/erpnext that referenced this pull request Jan 13, 2026
Adds support for the new PaymentController interface from frappe/payments.
This enables Payment Request to work with v2 payment gateways (like the
refactored Stripe integration) while maintaining full backward compatibility
with existing v1 gateways.

Changes:
- Add _is_v2_gateway() helper to detect PaymentController-based gateways
- Branch before_submit to use appropriate flow (v2 vs v1)
- Add _process_v2_gateway() method using PaymentController.initiate()
- Add get_tx_data() to prepare standardized transaction data

Related: frappe/payments#192
0spinboson added a commit to nlvegan/erpnext that referenced this pull request Jan 13, 2026
Adds support for the new PaymentController interface from frappe/payments.
This enables Payment Request to work with v2 payment gateways (like the
refactored Stripe integration) while maintaining full backward compatibility
with existing v1 gateways.

Changes:
- Add _is_v2_gateway() helper to detect PaymentController-based gateways
- Branch before_submit to use appropriate flow (v2 vs v1)
- Add _process_v2_gateway() method using PaymentController.initiate()
- Add get_tx_data() to prepare standardized transaction data

Related: frappe/payments#192
0spinboson added a commit to nlvegan/erpnext that referenced this pull request Jan 13, 2026
Adds support for the new PaymentController interface from frappe/payments.
This enables Payment Request to work with v2 payment gateways (like the
refactored Stripe integration) while maintaining full backward compatibility
with existing v1 gateways.

Changes:
- Add _is_v2_gateway() helper to detect PaymentController-based gateways
- Branch before_submit to use appropriate flow (v2 vs v1)
- Add _process_v2_gateway() method using PaymentController.initiate()
- Add get_tx_data() to prepare standardized transaction data

Related: frappe/payments#192
0spinboson added a commit to nlvegan/erpnext that referenced this pull request Jan 13, 2026
Adds support for the new PaymentController interface from frappe/payments.
This enables Payment Request to work with v2 payment gateways (like the
refactored Stripe integration) while maintaining full backward compatibility
with existing v1 gateways.

Changes:
- Add _is_v2_gateway() helper to detect PaymentController-based gateways
- Branch before_submit to use appropriate flow (v2 vs v1)
- Add _process_v2_gateway() method using PaymentController.initiate()
- Add get_tx_data() to prepare standardized transaction data

Related: frappe/payments#192
@0spinboson 0spinboson changed the title feat(stripe): Complete Stripe modernization with PaymentIntent API, webhooks, and security fixes refactor: PaymentController architecture with modernized Stripe integration Jan 14, 2026
@0spinboson 0spinboson force-pushed the refactor/payment-controller-architecture branch 3 times, most recently from e8e80ae to 9453739 Compare January 15, 2026 10:28
0spinboson added a commit to nlvegan/erpnext that referenced this pull request Jan 15, 2026
Adds support for the new PaymentController interface from frappe/payments.
This enables Payment Request to work with v2 payment gateways (like the
refactored Stripe integration) while maintaining full backward compatibility
with existing v1 gateways.

Core changes:
- Add _is_v2_gateway() helper delegating to payments.utils.is_v2_gateway()
- Branch before_submit to use appropriate flow (v2 vs v1)
- Add _process_v2_gateway() method using PaymentController.initiate()
- Add get_tx_data() to prepare standardized transaction data
- Store PSL reference for debugging and reconciliation

Performance optimization:
- Consolidate existence check with document fetch (single DB call)

Security & privacy hardening:
- Whitelist only payment-relevant contact fields (first_name, last_name,
  email_id, email, phone) instead of exposing full Contact.as_dict()
- Whitelist only payment-relevant address fields (address_line1/2, city,
  state, pincode, country) instead of full Address.as_dict()
- Add catch-all exception handler to prevent submission failures
- Log full exception server-side, show generic message to users

Compatibility:
- Add 'email' key alongside 'email_id' for gateway compatibility
- Normalize first_name to prevent None in tx_data
- Use get_request_amount() for partial payment support

Test coverage:
- v2 gateway detection (None, empty string, nonexistent, v1, v2)
- Flow selection (v2 vs v1 mutual exclusion)
- tx_data structure validation
- Partial payment amount handling
- PII minimization (whitelisted fields only)
- Exception handling

Related: frappe/payments#192
@0spinboson
Copy link
Copy Markdown
Author

okay, should be done now. Reorganized code changes logically into commits. Apologies for not doing this in my own fork first.

@0spinboson 0spinboson force-pushed the refactor/payment-controller-architecture branch 2 times, most recently from fc7471c to 9947cc7 Compare January 16, 2026 00:15
0spinboson added a commit to nlvegan/frappe_io that referenced this pull request Jan 19, 2026
Adds documentation for the new PaymentController v2 architecture in the
payments app, covering:

- Architecture overview and components
- Integration with ERPNext Payment Request
- Implementing v2 payment gateways
- Stripe v2 configuration guide
- API reference
- Troubleshooting guide

Related: frappe/erpnext#51723, frappe/payments#192
0spinboson added a commit to nlvegan/erpnext that referenced this pull request Jan 19, 2026
Adds support for the new PaymentController interface from frappe/payments.
This enables Payment Request to work with v2 payment gateways (like the
refactored Stripe integration) while maintaining full backward compatibility
with existing v1 gateways.

Core changes:
- Add _is_v2_gateway() helper delegating to payments.utils.is_v2_gateway()
- Branch before_submit to use appropriate flow (v2 vs v1)
- Add _process_v2_gateway() method using PaymentController.initiate()
- Add get_tx_data() to prepare standardized transaction data
- Store PSL reference for debugging and reconciliation

Performance optimization:
- Consolidate existence check with document fetch (single DB call)

Security & privacy hardening:
- Whitelist only payment-relevant contact fields (first_name, last_name,
  email_id, email, phone) instead of exposing full Contact.as_dict()
- Whitelist only payment-relevant address fields (address_line1/2, city,
  state, pincode, country) instead of full Address.as_dict()
- Add catch-all exception handler to prevent submission failures
- Log full exception server-side, show generic message to users

Compatibility:
- Add 'email' key alongside 'email_id' for gateway compatibility
- Normalize first_name to prevent None in tx_data
- Use get_request_amount() for partial payment support

Test coverage:
- v2 gateway detection (None, empty string, nonexistent, v1, v2)
- Flow selection (v2 vs v1 mutual exclusion)
- tx_data structure validation
- Partial payment amount handling
- PII minimization (whitelisted fields only)
- Exception handling

Related: frappe/payments#192
@0spinboson 0spinboson force-pushed the refactor/payment-controller-architecture branch 3 times, most recently from f8927b6 to f82df10 Compare January 20, 2026 23:02
@0spinboson 0spinboson force-pushed the refactor/payment-controller-architecture branch from f82df10 to 6263703 Compare January 20, 2026 23:03
0spinboson added a commit to nlvegan/erpnext that referenced this pull request Feb 20, 2026
Add support for the new PaymentController interface from frappe/payments,
enabling Payment Request to work with v2 gateways while maintaining
backward compatibility with v1.

Related: frappe/payments#192
0spinboson added a commit to nlvegan/erpnext that referenced this pull request Feb 20, 2026
Add support for the new PaymentController interface from frappe/payments,
enabling Payment Request to work with v2 gateways while maintaining
backward compatibility with v1.

Related: frappe/payments#192
@0spinboson
Copy link
Copy Markdown
Author

(I will update this PR tonight or tomorrow as I found a few more issues to address and code to refactor. just fyi.)

@0spinboson 0spinboson marked this pull request as draft February 20, 2026 11:56
@0spinboson 0spinboson force-pushed the refactor/payment-controller-architecture branch 2 times, most recently from 9d33c09 to 15a8dfc Compare February 27, 2026 09:06
@0spinboson 0spinboson changed the title refactor: PaymentController architecture with modernized Stripe integration feat: add PaymentController v2 architecture Feb 27, 2026
Add a template-method based PaymentController that orchestrates the
full payment lifecycle: initiate -> proceed -> process_response.

- PaymentController base class with abstract gateway contracts
- Payment Session Log DocType for tracking payment state
- Payment Button DocType for gateway selection UI
- /pay universal payment endpoint with multi-gateway support
- Type system (TxData, Proceeded, Processed, SessionStates)
- Custom exception hierarchy for structured error handling
- TX data filtering whitelist to prevent parameter tampering
- Concurrency guards with document locking and terminal state checks
- is_v2_gateway() utility for detecting v2-compatible gateways
- Fix delete_custom_fields to use composite key lookup
- Fix get_checkout_url to route through get_payment_gateway_controller
- Add Payment Session Log link field on Payment Request
- Integration tests for controller lifecycle
@0spinboson 0spinboson force-pushed the refactor/payment-controller-architecture branch from 15a8dfc to cdf1bbc Compare February 27, 2026 09:25
@0spinboson 0spinboson marked this pull request as ready for review February 27, 2026 09:29
0spinboson added a commit to nlvegan/erpnext that referenced this pull request Apr 18, 2026
Add support for the new PaymentController interface from frappe/payments,
enabling Payment Request to work with v2 gateways while maintaining
backward compatibility with v1.

Related: frappe/payments#192
0spinboson added a commit to nlvegan/erpnext that referenced this pull request May 4, 2026
Add support for the new PaymentController interface from frappe/payments,
enabling Payment Request to work with v2 gateways while maintaining
backward compatibility with v1.

Related: frappe/payments#192
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants