Skip to content

feat: pluggable pay driver for "Pay and Submit" (backport #53)#57

Merged
mergify[bot] merged 3 commits into
version-15-hotfixfrom
mergify/bp/version-15-hotfix/pr-53
Jul 5, 2026
Merged

feat: pluggable pay driver for "Pay and Submit" (backport #53)#57
mergify[bot] merged 3 commits into
version-15-hotfixfrom
mergify/bp/version-15-hotfix/pr-53

Conversation

@mergify

@mergify mergify Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

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_payment relabels the primary action to Pay and Submitfrm.savesubmit(), and the payout runs as the backend's on_submit side effect (submit-first). A backend that needs pay-first (e.g. an interactive bank OTP ceremony) had to fight this UX at runtime.

How

  • New public/js/pay_drivers.js: a client registry keyed by integration_doctyperegister_pay_driver(doctype, driver) / get_pay_driver(doctype). A driver is { form(frm), bulk(list_view, docs), add_fields }.
  • Form: the primary action resolves the driver's form handler; default stays frm.savesubmit(). The action now also flips live when make_bank_online_payment is toggled, not only on reload.
  • List: merges each registered driver's add_fields, and routes a selection per integration_doctype — driver-owned docs go to the driver's bulk, the rest run the existing OTP + bulk_pay_and_submit path. 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 pass node --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).

vorasmit and others added 3 commits July 5, 2026 09:38
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)
@mergify

mergify Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@mergify mergify Bot merged commit d009d59 into version-15-hotfix Jul 5, 2026
6 checks passed
@resilient-tech-bot

Copy link
Copy Markdown
Collaborator

🎉 This PR is included in version 15.2.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@greptile-apps

greptile-apps Bot commented Jul 5, 2026

Copy link
Copy Markdown

Confidence Score: 3/5

Safe on the happy path (no driver, or driver with both form + bulk), but a partially-implemented driver will silently route payments through the wrong server endpoint.

The registry and form-side changes are clean and backward-compatible. The list-side routing has a gap: a driver that ships form without bulk (a realistic intermediate state) will have its documents fall into default_bulk, pass the can_make_payment check, and be submitted via the RazorpayX bulk_pay_and_submit API — the wrong endpoint for any pay-first backend. A console.warn fires but the incorrect call still executes, meaning real money movement could go through the wrong handler without obvious user-visible failure.

payment_entry_list.js — specifically the partition_by_driver fallback path for form-only drivers.

Important Files Changed

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

Comment on lines +76 to +86
} 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);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 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.

Comment on lines +58 to +61
function pay_driver_fields() {
const drivers = payment_integration_utils.pay_drivers || {};
return Object.values(drivers).flatMap((driver) => driver.add_fields || []);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 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.

Suggested change
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!

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants