@@ -31,8 +31,16 @@ function stripCardNumberForNative(formatted: string): string {
3131 return formatted . replace ( / \s / g, '' ) ;
3232}
3333
34+ // Display is MM/YY but Android's validator requires MM/YYYY; iOS accepts both.
35+ // Expand only once the year is fully typed (4-char "MM/YY"); partial edits
36+ // pass through unchanged so native can keep reporting "cannot be blank".
37+ function expandExpiryYearForNative ( formatted : string ) : string {
38+ const match = / ^ ( \d { 2 } ) \/ ( \d { 2 } ) $ / . exec ( formatted ) ;
39+ return match ? `${ match [ 1 ] } /20${ match [ 2 ] } ` : formatted ;
40+ }
41+
3442export function useCardForm ( options : UseCardFormOptions = { } ) : UseCardFormReturn {
35- const { collectCardholderName = false , onValidationChange, onMetadataChange, onBinDataChange } = options ;
43+ const { onValidationChange, onMetadataChange, onBinDataChange } = options ;
3644 const { isReady } = usePrimerCheckout ( ) ;
3745
3846 // Field state
@@ -80,12 +88,12 @@ export function useCardForm(options: UseCardFormOptions = {}): UseCardFormReturn
8088 } ) ;
8189
8290 // Debounced sync to native
83- const debouncedRef = useRef <
84- DebouncedFunction < ( data : { cardNumber : string ; expiryDate : string ; cvv : string ; cardholderName ? : string } ) => void >
85- > ( null as any ) ;
91+ const debouncedRef = useRef < DebouncedFunction <
92+ ( data : { cardNumber : string ; expiryDate : string ; cvv : string ; cardholderName : string } ) => void
93+ > | null > ( null ) ;
8694 useEffect ( ( ) => {
8795 debouncedRef . current = debounce (
88- ( data : { cardNumber : string ; expiryDate : string ; cvv : string ; cardholderName ? : string } ) => {
96+ ( data : { cardNumber : string ; expiryDate : string ; cvv : string ; cardholderName : string } ) => {
8997 bridge . setRawData ( data ) . catch ( ( ) => { } ) ;
9098 } ,
9199 DEBOUNCE_MS
@@ -98,16 +106,13 @@ export function useCardForm(options: UseCardFormOptions = {}): UseCardFormReturn
98106
99107 const syncToNative = useCallback ( ( ) => {
100108 const fields = fieldsRef . current ;
101- const data : { cardNumber : string ; expiryDate : string ; cvv : string ; cardholderName ?: string } = {
109+ debouncedRef . current ?. ( {
102110 cardNumber : stripCardNumberForNative ( fields . cardNumber ) ,
103- expiryDate : fields . expiryDate ,
111+ expiryDate : expandExpiryYearForNative ( fields . expiryDate ) ,
104112 cvv : fields . cvv ,
105- } ;
106- if ( collectCardholderName ) {
107- data . cardholderName = fields . cardholderName ;
108- }
109- debouncedRef . current ?.( data ) ;
110- } , [ collectCardholderName ] ) ;
113+ cardholderName : fields . cardholderName ,
114+ } ) ;
115+ } , [ ] ) ;
111116
112117 // Updaters
113118 const updateCardNumber = useCallback (
@@ -197,7 +202,7 @@ export function useCardForm(options: UseCardFormOptions = {}): UseCardFormReturn
197202 setBinData ( null ) ;
198203 fieldsRef . current = { cardNumber : '' , expiryDate : '' , cvv : '' , cardholderName : '' } ;
199204 debouncedRef . current ?. cancel ( ) ;
200- bridge . setRawData ( { cardNumber : '' , expiryDate : '' , cvv : '' } ) . catch ( ( ) => { } ) ;
205+ bridge . setRawData ( { cardNumber : '' , expiryDate : '' , cvv : '' , cardholderName : '' } ) . catch ( ( ) => { } ) ;
201206 } , [ bridge . setRawData ] ) ;
202207
203208 return {
0 commit comments