diff --git a/Lib/Gateway/Paypal_Gateway.php b/Lib/Gateway/Paypal_Gateway.php
index 3180b89eb..26c4263cc 100644
--- a/Lib/Gateway/Paypal_Gateway.php
+++ b/Lib/Gateway/Paypal_Gateway.php
@@ -67,7 +67,7 @@ protected function init() {
$this->id = 'paypal';
$this->admin_label = __( 'PayPal', 'wp-user-frontend' );
$this->checkout_label = __( 'PayPal', 'wp-user-frontend' );
- $this->icon = apply_filters( 'wpuf_paypal_checkout_icon', WPUF_ASSET_URI . '/images/paypal.png' );
+ $this->icon = apply_filters( 'wpuf_paypal_checkout_icon', WPUF_ASSET_URI . '/images/paypal-mark.svg' );
$this->supports_subscription = true;
// Initialize PayPal-specific properties
diff --git a/Lib/WeDevs_Settings_API.php b/Lib/WeDevs_Settings_API.php
index 05347bae2..0d701ab36 100644
--- a/Lib/WeDevs_Settings_API.php
+++ b/Lib/WeDevs_Settings_API.php
@@ -344,6 +344,85 @@ function callback_multicheck( $args ) {
echo wp_kses( $html, array('fieldset' => [],'label' => ['for' => []],'input' => ['type' => [],'class' => [],'id' => [],'name' => [],'value' => [],'checked' => [],],'br' => [],'span' => ['class' => []],'svg' => ['width' => [],'height' => [],'viewBox' => [],'fill' => [],'xmlns' => [],],'path' => ['d' => [], 'fill' => []],'p' => ['class' => [] ] ) );
}
+ /**
+ * Displays a Texty-style card grid for selecting payment gateways
+ *
+ * Renders each gateway as a clickable card with icon and name.
+ * Multiple cards can be checked (multi-select). Clicking a card
+ * also reveals that gateway's settings panel below the grid.
+ *
+ * @since WPUF_SINCE
+ *
+ * @param array $args settings field args
+ *
+ * @return void
+ */
+ function callback_gateway_selector( $args ) {
+ $value = $this->get_option( $args['id'], $args['section'], $args['std'] );
+ $value = $value ? $value : [];
+
+ // Inline SVG fallback icons (no image files exist for these)
+ $bank_svg = '';
+ $generic_svg = '';
+ ?>
+
+
\ No newline at end of file
diff --git a/assets/js/admin/settings.js b/assets/js/admin/settings.js
index f02ae4839..964533b21 100644
--- a/assets/js/admin/settings.js
+++ b/assets/js/admin/settings.js
@@ -59,6 +59,9 @@
close.style.display = 'none';
})
+ // Gateway Selector Card Grid
+ wpufInitGatewaySelector();
+
function wpuf_search_reset() {
content.forEach(function (row, index) {
var content_id = row.closest('div').getAttribute('id');
@@ -73,4 +76,232 @@
}
});
+
+ /**
+ * Gateway Selector Card Grid
+ *
+ * Handles card click to toggle checkbox (multi-select) and
+ * shows only the clicked gateway's settings rows below.
+ */
+ function wpufInitGatewaySelector() {
+ var container = document.querySelector('.wpuf-gateway-cards');
+
+ if ( ! container ) {
+ return;
+ }
+
+ // Map gateway IDs to their settings field name prefixes.
+ // PayPal fields: paypal_*, gate_instruct_paypal
+ // Bank fields: bank_*, gate_instruct_bank
+ // Generic pattern: field name contains the gateway ID
+ var gatewayCards = container.querySelectorAll('.wpuf-gateway-card');
+
+ // Find the that contains the gateway selector itself
+ var selectorRow = container.closest('tr');
+
+ if ( ! selectorRow ) {
+ return;
+ }
+
+ // Collect all
siblings after the selector row in the same table
+ var formTable = selectorRow.closest('table');
+ var allRows = formTable ? formTable.querySelectorAll('tr') : [];
+ var afterRows = [];
+ var pastSelector = false;
+
+ allRows.forEach(function(row) {
+ if ( row === selectorRow ) {
+ pastSelector = true;
+ return;
+ }
+
+ if ( pastSelector ) {
+ afterRows.push(row);
+ }
+ });
+
+ // Fields whose names don't contain a gateway ID but belong to one
+ var fieldGatewayMap = {
+ 'failed_retry': 'paypal',
+ };
+
+ /**
+ * Determine which gateway a settings row belongs to by
+ * checking the name attribute of inputs/selects/textareas inside it.
+ */
+ function getRowGatewayId(row) {
+ var inputs = row.querySelectorAll('input, select, textarea');
+ var gatewayIds = [];
+
+ gatewayCards.forEach(function(card) {
+ gatewayIds.push(card.getAttribute('data-gateway'));
+ });
+
+ for ( var i = 0; i < inputs.length; i++ ) {
+ var name = inputs[i].getAttribute('name') || '';
+ // Extract field name from wpuf_payment[field_name]
+ var match = name.match(/\[([^\]]+)\]$/);
+
+ if ( match ) {
+ var fieldName = match[1];
+
+ // Check explicit field-to-gateway mapping first
+ if ( fieldGatewayMap[fieldName] ) {
+ return fieldGatewayMap[fieldName];
+ }
+
+ for ( var j = 0; j < gatewayIds.length; j++ ) {
+ var gid = gatewayIds[j];
+
+ // Match: gate_instruct_paypal, paypal_email, bank_success, etc.
+ if ( fieldName.indexOf(gid) !== -1 || fieldName.indexOf('gate_instruct_' + gid) !== -1 ) {
+ return gid;
+ }
+ }
+ }
+ }
+
+ // Check the | label text for a gateway ID match
+ var thLabel = row.querySelector('th');
+ if ( thLabel ) {
+ var labelText = thLabel.getAttribute('scope') === 'row' ? (thLabel.textContent || '') : '';
+
+ for ( var k = 0; k < gatewayIds.length; k++ ) {
+ if ( labelText.toLowerCase().indexOf(gatewayIds[k]) !== -1 ) {
+ return gatewayIds[k];
+ }
+ }
+
+ // Check the label[for] attribute (e.g. "wpuf_payment[paypal_webhook_events_info]")
+ var labelEl = thLabel.querySelector('label[for]');
+ if ( labelEl ) {
+ var forAttr = labelEl.getAttribute('for') || '';
+ var forMatch = forAttr.match(/\[([^\]]+)\]$/);
+ if ( forMatch ) {
+ var forFieldName = forMatch[1];
+
+ if ( fieldGatewayMap[forFieldName] ) {
+ return fieldGatewayMap[forFieldName];
+ }
+
+ for ( var m = 0; m < gatewayIds.length; m++ ) {
+ if ( forFieldName.indexOf(gatewayIds[m]) !== -1 ) {
+ return gatewayIds[m];
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ // Tag each row with its gateway ID
+ afterRows.forEach(function(row) {
+ var gid = getRowGatewayId(row);
+
+ if ( gid ) {
+ row.classList.add('wpuf-gateway-setting-row');
+ row.setAttribute('data-gateway-id', gid);
+ }
+ });
+
+ /**
+ * Update the focused (green border) state on gateway cards
+ */
+ function setFocusedCard(gatewayId) {
+ gatewayCards.forEach(function(card) {
+ if ( card.getAttribute('data-gateway') === gatewayId ) {
+ card.classList.add('wpuf-gateway-card--focused');
+ } else {
+ card.classList.remove('wpuf-gateway-card--focused');
+ }
+ });
+ }
+
+ /**
+ * Show settings rows for a specific gateway, hide others
+ */
+ function showGatewaySettings(gatewayId) {
+ afterRows.forEach(function(row) {
+ if ( ! row.classList.contains('wpuf-gateway-setting-row') ) {
+ return;
+ }
+
+ if ( row.getAttribute('data-gateway-id') === gatewayId ) {
+ row.classList.remove('wpuf-gateway-setting-hidden');
+ } else {
+ row.classList.add('wpuf-gateway-setting-hidden');
+ }
+ });
+
+ setFocusedCard(gatewayId);
+ }
+
+ /**
+ * Hide all gateway-specific settings rows
+ */
+ function hideAllGatewaySettings() {
+ afterRows.forEach(function(row) {
+ if ( row.classList.contains('wpuf-gateway-setting-row') ) {
+ row.classList.add('wpuf-gateway-setting-hidden');
+ }
+ });
+
+ setFocusedCard(null);
+ }
+
+ // Checkbox change handler (triggered by the |