Skip to content

Commit 837b0d7

Browse files
committed
feat: add PHPStan static analysis and PayPal classic mode
Infrastructure & Dev Tools: - Add PHPStan static analysis with WordPress and WooCommerce stubs - Configure pre-commit hooks with Husky for automated checks - Add PHP linting scripts to package.json and composer.json - Add PHPStan configuration and bootstrap files PayPal Enhancements: - Implement PayPal classic checkout mode for non-redirect flow - Add JSON validation for PayPal style settings - Add redirect_flow property to control checkout behavior - Enqueue PayPal-specific scripts conditionally Mobile UX Improvements: - Make payment method labels responsive with column layout on mobile - Align label container to flex-start on small screens Code Quality & Refactoring: - Clean up admin notice template structure - Improve subscription handler interfaces and implementations - Refactor Apple/Google Pay components and blocks - Update webpack configuration for better build process - Improve error handling and type safety across payment gateways Settings Improvements: - Add Apple/Google Pay settings updates - Enhance payment method configuration options
1 parent f8fd9b5 commit 837b0d7

37 files changed

+2251
-1117
lines changed

.husky/pre-commit

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/usr/bin/env sh
2+
3+
# Get list of staged PHP files
4+
STAGED_PHP_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.php$')
5+
6+
if [ -n "$STAGED_PHP_FILES" ]; then
7+
echo "Running PHPStan on staged PHP files..."
8+
9+
# Run PHPStan only on staged files with memory limit
10+
vendor/bin/phpstan analyse $STAGED_PHP_FILES --error-format=table --memory-limit=1G
11+
12+
if [ $? -ne 0 ]; then
13+
echo "❌ PHPStan found errors. Please fix them before committing."
14+
exit 1
15+
fi
16+
17+
echo "✅ PHPStan passed!"
18+
fi

assets/css/monei-blocks-checkout.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,9 @@
155155
.monei-card-brands {
156156
margin-left: auto;
157157
}
158+
159+
.monei-label-container {
160+
flex-direction: column;
161+
align-items: flex-start;
162+
}
158163
}
Lines changed: 134 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -1,136 +1,147 @@
1-
import { getPlaceOrderButton, useButtonStateManager } from '../helpers/monei-shared-utils';
1+
import {
2+
getPlaceOrderButton,
3+
useButtonStateManager,
4+
} from '../helpers/monei-shared-utils';
25

36
/**
47
* Create Apple/Google Pay label
58
* @param {Object} moneiData - Configuration data
6-
* @returns {React.Element}
9+
* @return {React.Element}
710
*/
811
export const createAppleGoogleLabel = ( moneiData ) => {
9-
const isApple = window.ApplePaySession?.canMakePayments?.();
10-
const appleEnabled = moneiData.logo_apple !== false;
11-
const googleEnabled = moneiData.logo_google !== false;
12-
13-
let logo = googleEnabled ? moneiData.logo_google : false;
14-
logo = isApple && appleEnabled ? moneiData.logo_apple : logo;
15-
16-
const title = isApple && appleEnabled ? 'Apple Pay' : 'Google Pay';
17-
const shouldShowLogo = ( isApple && moneiData?.logo_apple ) ||
18-
( ! isApple && moneiData?.logo_google );
19-
20-
return (
21-
<div className="monei-label-container">
22-
<span className="monei-text">{ title }</span>
23-
{ shouldShowLogo && (
24-
<div className="monei-logo">
25-
<img src={ logo } alt="" />
26-
</div>
27-
) }
28-
</div>
29-
);
12+
const isApple = window.ApplePaySession?.canMakePayments?.();
13+
const appleEnabled = moneiData.logo_apple !== false;
14+
const googleEnabled = moneiData.logo_google !== false;
15+
16+
let logo = googleEnabled ? moneiData.logo_google : false;
17+
logo = isApple && appleEnabled ? moneiData.logo_apple : logo;
18+
19+
const title = isApple && appleEnabled ? 'Apple Pay' : 'Google Pay';
20+
const shouldShowLogo =
21+
( isApple && moneiData?.logo_apple ) ||
22+
( ! isApple && moneiData?.logo_google );
23+
24+
return (
25+
<div className="monei-label-container">
26+
<span className="monei-text">{ title }</span>
27+
{ shouldShowLogo && (
28+
<div className="monei-logo">
29+
<img src={ logo } alt="" />
30+
</div>
31+
) }
32+
</div>
33+
);
3034
};
3135

3236
/**
3337
* Shared Apple/Google Pay Content Component
3438
* @param {Object} props - Component props
35-
* @returns {React.Element}
39+
* @return {React.Element}
3640
*/
3741
export const MoneiAppleGoogleContent = ( props ) => {
38-
const { useEffect, useRef } = wp.element;
39-
const { responseTypes } = props.emitResponse;
40-
const { onPaymentSetup } = props.eventRegistration;
41-
const { activePaymentMethod } = props;
42-
const moneiData = props.moneiData || wc.wcSettings.getSetting( 'monei_apple_google_data' );
43-
44-
const paymentRequestRef = useRef( null );
45-
const isActive = activePaymentMethod === ( props.paymentMethodId || 'monei_apple_google' );
46-
47-
const buttonManager = useButtonStateManager( {
48-
isActive,
49-
emitResponse: props.emitResponse,
50-
tokenFieldName: 'monei_payment_request_token',
51-
errorMessage: moneiData.tokenErrorString
52-
} );
53-
54-
/**
55-
* Initialize MONEI Payment Request
56-
*/
57-
const initPaymentRequest = () => {
58-
if ( typeof monei === 'undefined' || ! monei.PaymentRequest ) {
59-
console.error( 'MONEI SDK is not available' );
60-
return;
61-
}
62-
63-
// Clean up existing instance
64-
if ( paymentRequestRef.current?.close ) {
65-
try {
66-
paymentRequestRef.current.close();
67-
} catch ( e ) {
68-
// Silent fail
69-
}
70-
}
71-
72-
const paymentRequest = monei.PaymentRequest( {
73-
accountId: moneiData.accountId,
74-
sessionId: moneiData.sessionId,
75-
language: moneiData.language,
76-
amount: Math.round( moneiData.total * 100 ),
77-
currency: moneiData.currency,
78-
onSubmit( result ) {
79-
if ( result.token ) {
80-
buttonManager.enableCheckout( result.token );
81-
}
82-
},
83-
onError( error ) {
84-
console.error( error );
85-
}
86-
} );
87-
88-
const container = document.getElementById( 'payment-request-container' );
89-
if ( container ) {
90-
paymentRequest.render( container );
91-
paymentRequestRef.current = paymentRequest;
92-
}
93-
};
94-
95-
// Initialize on mount
96-
useEffect( () => {
97-
initPaymentRequest();
98-
99-
return () => {
100-
if ( paymentRequestRef.current?.close ) {
101-
try {
102-
paymentRequestRef.current.close();
103-
} catch ( e ) {
104-
// Silent cleanup
105-
}
106-
}
107-
};
108-
}, [] );
109-
110-
// Setup payment hook
111-
useEffect( () => {
112-
const unsubscribe = onPaymentSetup( () => {
113-
return buttonManager.getPaymentData();
114-
} );
115-
116-
return () => unsubscribe();
117-
}, [ onPaymentSetup, buttonManager.tokenRef.current ] );
118-
119-
return (
120-
<fieldset className="monei-fieldset monei-payment-request-fieldset">
121-
<div
122-
id="payment-request-container"
123-
className="monei-payment-request-container"
124-
>
125-
{ /* Payment button will be rendered here */ }
126-
</div>
127-
<input
128-
type="hidden"
129-
id="monei_payment_token"
130-
name="monei_payment_token"
131-
value=""
132-
/>
133-
<div id="monei-card-error" className="monei-error" />
134-
</fieldset>
135-
);
136-
};
42+
const { useEffect, useRef } = wp.element;
43+
const { responseTypes } = props.emitResponse;
44+
const { onPaymentSetup } = props.eventRegistration;
45+
const { activePaymentMethod } = props;
46+
const moneiData =
47+
props.moneiData ||
48+
wc.wcSettings.getSetting( 'monei_apple_google_data' );
49+
50+
const paymentRequestRef = useRef( null );
51+
const isActive =
52+
activePaymentMethod ===
53+
( props.paymentMethodId || 'monei_apple_google' );
54+
55+
const buttonManager = useButtonStateManager( {
56+
isActive,
57+
emitResponse: props.emitResponse,
58+
tokenFieldName: 'monei_payment_request_token',
59+
errorMessage: moneiData.tokenErrorString,
60+
} );
61+
62+
/**
63+
* Initialize MONEI Payment Request
64+
*/
65+
const initPaymentRequest = () => {
66+
if ( typeof monei === 'undefined' || ! monei.PaymentRequest ) {
67+
console.error( 'MONEI SDK is not available' );
68+
return;
69+
}
70+
71+
// Clean up existing instance
72+
if ( paymentRequestRef.current?.close ) {
73+
try {
74+
paymentRequestRef.current.close();
75+
} catch ( e ) {
76+
// Silent fail
77+
}
78+
}
79+
80+
const paymentRequest = monei.PaymentRequest( {
81+
accountId: moneiData.accountId,
82+
sessionId: moneiData.sessionId,
83+
language: moneiData.language,
84+
amount: Math.round( moneiData.total * 100 ),
85+
currency: moneiData.currency,
86+
style: moneiData.paymentRequestStyle || {},
87+
onSubmit( result ) {
88+
if ( result.token ) {
89+
buttonManager.enableCheckout( result.token );
90+
}
91+
},
92+
onError( error ) {
93+
console.error( error );
94+
},
95+
} );
96+
97+
const container = document.getElementById(
98+
'payment-request-container'
99+
);
100+
if ( container ) {
101+
paymentRequest.render( container );
102+
paymentRequestRef.current = paymentRequest;
103+
}
104+
};
105+
106+
// Initialize on mount
107+
useEffect( () => {
108+
initPaymentRequest();
109+
110+
return () => {
111+
if ( paymentRequestRef.current?.close ) {
112+
try {
113+
paymentRequestRef.current.close();
114+
} catch ( e ) {
115+
// Silent cleanup
116+
}
117+
}
118+
};
119+
}, [] );
120+
121+
// Setup payment hook
122+
useEffect( () => {
123+
const unsubscribe = onPaymentSetup( () => {
124+
return buttonManager.getPaymentData();
125+
} );
126+
127+
return () => unsubscribe();
128+
}, [ onPaymentSetup, buttonManager.tokenRef.current ] );
129+
130+
return (
131+
<fieldset className="monei-fieldset monei-payment-request-fieldset">
132+
<div
133+
id="payment-request-container"
134+
className="monei-payment-request-container"
135+
>
136+
{ /* Payment button will be rendered here */ }
137+
</div>
138+
<input
139+
type="hidden"
140+
id="monei_payment_token"
141+
name="monei_payment_token"
142+
value=""
143+
/>
144+
<div id="monei-card-error" className="monei-error" />
145+
</fieldset>
146+
);
147+
};

0 commit comments

Comments
 (0)