feat: pluggable pay driver for "Pay and Submit" (backport #53)#57
Conversation
Backends can now register a pay driver keyed by integration_doctype to swap what "Pay and Submit" does, without touching the button, label, checkbox toggle, or dialogs. No driver registered keeps the current RazorpayX behaviour (savesubmit on the form, OTP + bulk_pay_and_submit on the list). The form primary action and the list bulk action resolve the driver from the registry; the list also merges each driver's add_fields and routes a mixed selection per integration_doctype. The primary action now also flips live when make_bank_online_payment is toggled, not only on reload. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit 4d2ce31)
Routing a mixed selection ran every matching driver's bulk (and the default flow) in the same tick, stacking their confirm/OTP dialogs and starting two money-moving batches at once. Refuse a selection that spans more than one flow and ask the user to narrow it; run the single flow otherwise. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit 4d33a56)
Address review on #53: - Mixed selection now alerts and runs one payment flow, rather than refusing the whole selection (per @vorasmit). - Count the default path as a flow only when it has a payable doc, so a batch that merely includes ineligible rows no longer trips the alert (per greptile); ineligible rows still surface in the default confirm. - Warn when a driver registers a form handler but no bulk handler, which would otherwise pay via the wrong default path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit 2cc562e)
|
Tick the box to add this pull request to the merge queue (same as
|
|
🎉 This PR is included in version 15.2.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Confidence Score: 3/5Safe on the happy path (no driver, or driver with both The registry and form-side changes are clean and backward-compatible. The list-side routing has a gap: a driver that ships
|
| Filename | Overview |
|---|---|
| payment_integration_utils/payment_integration_utils/client_overrides/list/payment_entry_list.js | Introduces driver-based routing for bulk Pay and Submit; a driver with only a form handler silently falls through to the RazorpayX bulk_pay_and_submit endpoint, and pay_driver_fields() does not deduplicate returned field names. |
| payment_integration_utils/payment_integration_utils/client_overrides/form/payment_entry.js | Adds live checkbox toggle for primary action label and wires driver's form handler; logic is clean with proper guard conditions. |
| payment_integration_utils/public/js/pay_drivers.js | New driver registry module; straightforward namespace initialization, register/get helpers, and backward-compatible null return for missing drivers. |
| payment_integration_utils/public/js/payment_integration_utils.bundle.js | Adds pay_drivers.js import to the bundle; no issues. |
Reviews (1): Last reviewed commit: "fix(payment-entry): alert on a mixed bul..." | Re-trigger Greptile
| } else { | ||
| // A driver with a form handler but no bulk handler would silently pay | ||
| // via the default path (wrong server API for a pay-first backend). | ||
| if (driver && !driver.bulk) { | ||
| console.warn( | ||
| `pay_driver for "${doc.integration_doctype}" has no bulk handler; ` + | ||
| `falling back to the default path for ${doc.name}.` | ||
| ); | ||
| } | ||
| defaults.push(doc); | ||
| } |
There was a problem hiding this comment.
Form-only driver docs silently processed by wrong backend
When a driver registers a form handler but omits bulk, partition_by_driver pushes those docs into defaults. If they pass can_make_payment (they will, since they have integration_docname/integration_doctype set), default_bulk puts them in marked_docs and ultimately fires the RazorpayX bulk_pay_and_submit server endpoint — which is the wrong API for any pay-first backend. The console.warn fires but the incorrect money-moving call still executes. A pay-first driver author who ships form first and bulk later would inadvertently route payments through the wrong server-side handler.
| function pay_driver_fields() { | ||
| const drivers = payment_integration_utils.pay_drivers || {}; | ||
| return Object.values(drivers).flatMap((driver) => driver.add_fields || []); | ||
| } |
There was a problem hiding this comment.
pay_driver_fields() flatMaps all driver field lists without deduplication. If two drivers request the same field (e.g. "integration_doctype" is already in the base add_fields and a driver also adds it), the field appears twice. While Frappe may tolerate duplicates, it's worth filtering to avoid surprising query-builder behaviour.
| function pay_driver_fields() { | |
| const drivers = payment_integration_utils.pay_drivers || {}; | |
| return Object.values(drivers).flatMap((driver) => driver.add_fields || []); | |
| } | |
| function pay_driver_fields() { | |
| const drivers = payment_integration_utils.pay_drivers || {}; | |
| const fields = Object.values(drivers).flatMap((driver) => driver.add_fields || []); | |
| return [...new Set(fields)]; | |
| } |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
What
Make the "Pay and Submit" action pluggable so a payment backend can change what it does without touching the button, label, checkbox toggle, or dialogs.
Today the flow is hard-wired: a checked
make_bank_online_paymentrelabels the primary action to Pay and Submit →frm.savesubmit(), and the payout runs as the backend'son_submitside effect (submit-first). A backend that needs pay-first (e.g. an interactive bank OTP ceremony) had to fight this UX at runtime.How
public/js/pay_drivers.js: a client registry keyed byintegration_doctype—register_pay_driver(doctype, driver)/get_pay_driver(doctype). A driver is{ form(frm), bulk(list_view, docs), add_fields }.formhandler; default staysfrm.savesubmit(). The action now also flips live whenmake_bank_online_paymentis toggled, not only on reload.add_fields, and routes a selection perintegration_doctype— driver-owned docs go to the driver'sbulk, the rest run the existing OTP +bulk_pay_and_submitpath. RazorpayX and another backend can coexist on one site.Backward compatibility
No driver registered = exactly today's behaviour. RazorpayX is untouched. No server change.
Testing
Pure client JS. Bundle builds clean (
bench build --app payment_integration_utils); all touched files passnode --check. Companion PR (bankbridge) registers the first real driver.🤖 Generated with Claude Code
This is an automatic backport of pull request #53 done by [Mergify](https://mergify.com).