-
Notifications
You must be signed in to change notification settings - Fork 69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
POC - Centralized payment method definitions #10217
Draft
brettshumaker
wants to merge
47
commits into
develop
Choose a base branch
from
poc/centralized-payment-method-definitions
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 42 commits
Commits
Show all changes
47 commits
Select commit
Hold shift + click to select a range
3849078
Sorry for the big commit - first pass at getting the php side working…
brettshumaker 888fcc6
We need to access this method outside of the definition.
brettshumaker 66ab9a1
Update afterpay class to use new definitions.
brettshumaker ab7c4fb
Fix dark icon file checking.
brettshumaker 845ecd6
Push generated types to client mapping
brettshumaker ae13734
Fix tests.
brettshumaker 9d0cc13
Fixing the tests revealed that the stripe_id here should just be the …
brettshumaker c3c093c
Add note to Afterpay
brettshumaker b0aece2
Make get_stripe_id a consistent trait for the definitions.
brettshumaker b0d8884
Use existing constant for definition ID.
brettshumaker 70ec9bd
Address notes from Marcin
brettshumaker 2baec4b
Revert "Address notes from Marcin"
brettshumaker 87e8f88
Rename classes to psr-4 and update references
brettshumaker ffe2a5d
Remove BNPL trait and definition.
brettshumaker 136a012
Remove Payment_Method_Icons trait and add direct icon URL methods to …
brettshumaker 45bc090
Remove Base_Payment_Method trait and move some of its functionality t…
brettshumaker 9ae210b
Moved to static methods and updated all references to reflect that.
brettshumaker df36af2
white space clean up and use the "correct" id for the Affirm payment …
brettshumaker 5258803
Update to definition registry, the definition registration process, a…
brettshumaker 8fd22ab
Make a new CLI Commands class for implementing our CLI command and an…
brettshumaker d436df5
Fix PSR-4 naming inconsistencies in includes/payment-methods/Configs
brettshumaker 22e3e4f
init payment definitions at run time
brettshumaker 58e1feb
Make PaymentMethodDefinitionRegistry consistently have instance methods
brettshumaker bf2c18c
Rearrange method order
brettshumaker db37a11
Make is_reusable/bnpl/accept_only_domestic_payment part of the defini…
brettshumaker be7c41f
Merge remote-tracking branch 'origin/develop' into poc/centralized-pa…
brettshumaker 76a18d2
Remove unused `use` statements
brettshumaker f3d88c0
Rename payment method definition method
brettshumaker 7795dff
Add support for manual capture to definition
brettshumaker ff219f6
Add manual capture to the definition interface - missed this in the l…
brettshumaker a035b82
Remove unneeded npm command and associated file.
brettshumaker 521b941
Simplify mapping class
brettshumaker fad6bde
Auto-generate icon components when generating types for payment methods.
brettshumaker 174689d
Fix psalm linting
brettshumaker b4e30c1
Merge branch 'develop' into poc/centralized-payment-method-definitions
brettshumaker c7b9b8e
Convert Klarna to new payment method definition
brettshumaker 81f31e0
Add utility method for checking if a given currency is "domestic" for…
brettshumaker ceb5657
Remove unused method in definition.
brettshumaker dccdc2d
Fix js tests
brettshumaker 8af7634
Merge branch 'develop' into poc/centralized-payment-method-definitions
brettshumaker 3069f05
Fix Klarna tests
brettshumaker ad0bc2e
Create poc-centralized-payment-method-definitions
brettshumaker ee07ebc
Merge branch 'develop' into poc/centralized-payment-method-definitions
frosso 5e06321
Revert "Fix js tests"
brettshumaker b3cd1dc
Actually fix js tests
brettshumaker a16c8f3
Resolve (compiling) circular dependency.
brettshumaker 7be446b
Merge branch 'develop' into poc/centralized-payment-method-definitions
brettshumaker File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
const { spawn } = require( 'child_process' ); | ||
const path = require( 'path' ); | ||
const fs = require( 'fs' ); | ||
|
||
// Helper function to recursively get all files in a directory | ||
function getAllFiles( dirPath, arrayOfFiles = [] ) { | ||
const files = fs.readdirSync( dirPath ); | ||
|
||
files.forEach( ( file ) => { | ||
const fullPath = path.join( dirPath, file ); | ||
if ( fs.statSync( fullPath ).isDirectory() ) { | ||
arrayOfFiles = getAllFiles( fullPath, arrayOfFiles ); | ||
} else { | ||
arrayOfFiles.push( fullPath ); | ||
} | ||
} ); | ||
|
||
return arrayOfFiles; | ||
} | ||
|
||
// Helper function to recursively remove a directory | ||
function removeDirectory( dirPath ) { | ||
if ( fs.existsSync( dirPath ) ) { | ||
fs.readdirSync( dirPath ).forEach( ( file ) => { | ||
const curPath = path.join( dirPath, file ); | ||
if ( fs.lstatSync( curPath ).isDirectory() ) { | ||
removeDirectory( curPath ); | ||
} else { | ||
fs.unlinkSync( curPath ); | ||
} | ||
} ); | ||
fs.rmdirSync( dirPath ); | ||
} | ||
} | ||
|
||
class PaymentMethodsPlugin { | ||
apply( compiler ) { | ||
// Track if we've built the payment methods in this session | ||
let hasBuilt = false; | ||
// Track if we're currently building | ||
let isBuilding = false; | ||
|
||
// Create build directory if it doesn't exist | ||
const buildDir = path.resolve( __dirname, './payment-methods' ); | ||
if ( ! fs.existsSync( buildDir ) ) { | ||
fs.mkdirSync( buildDir, { recursive: true } ); | ||
} | ||
|
||
// Add payment method files to webpack's watch list | ||
compiler.hooks.afterCompile.tap( | ||
'PaymentMethodsPlugin', | ||
( compilation ) => { | ||
const paymentMethodsDir = path.resolve( | ||
__dirname, | ||
'../includes/payment-methods/Configs' | ||
); | ||
|
||
// Add all files and directories recursively | ||
if ( fs.existsSync( paymentMethodsDir ) ) { | ||
// Add the root directory | ||
compilation.fileDependencies.add( paymentMethodsDir ); | ||
|
||
// Add all files recursively | ||
const allFiles = getAllFiles( paymentMethodsDir ); | ||
allFiles.forEach( ( file ) => { | ||
compilation.fileDependencies.add( file ); | ||
} ); | ||
|
||
// Add all directories recursively | ||
const addDirectory = ( dir ) => { | ||
compilation.fileDependencies.add( dir ); | ||
fs.readdirSync( dir ).forEach( ( file ) => { | ||
const fullPath = path.join( dir, file ); | ||
if ( fs.statSync( fullPath ).isDirectory() ) { | ||
addDirectory( fullPath ); | ||
} | ||
} ); | ||
}; | ||
addDirectory( paymentMethodsDir ); | ||
} | ||
} | ||
); | ||
|
||
const buildPaymentMethods = ( callback ) => { | ||
if ( isBuilding ) { | ||
callback(); | ||
return; | ||
} | ||
|
||
isBuilding = true; | ||
console.log( '\nBuilding payment method definitions...' ); | ||
|
||
// Run WP-CLI command inside Docker | ||
const cliScript = spawn( 'docker', [ | ||
'compose', | ||
'exec', | ||
'-T', | ||
'wordpress', | ||
'wp', | ||
'wcpay', | ||
'generate-payment-method-configs', | ||
'--allow-root', | ||
] ); | ||
|
||
cliScript.stdout.on( 'data', ( data ) => { | ||
console.log( `${ data }` ); | ||
} ); | ||
|
||
cliScript.stderr.on( 'data', ( data ) => { | ||
console.error( `Error: ${ data }` ); | ||
} ); | ||
|
||
cliScript.on( 'close', ( code ) => { | ||
if ( code !== 0 ) { | ||
console.error( 'WP-CLI command failed' ); | ||
isBuilding = false; | ||
callback( new Error( 'WP-CLI command failed' ) ); | ||
return; | ||
} | ||
|
||
console.log( 'WP-CLI command completed successfully' ); | ||
|
||
// Then run the JavaScript script | ||
const jsScript = spawn( | ||
'node', | ||
[ | ||
path.resolve( | ||
__dirname, | ||
'../includes/payment-methods/Configs/Scripts/generate-payment-method-types.js' | ||
), | ||
], | ||
{ | ||
stdio: 'inherit', | ||
} | ||
); | ||
|
||
jsScript.on( 'close', ( closeCode ) => { | ||
if ( closeCode !== 0 ) { | ||
console.error( 'JavaScript script failed' ); | ||
isBuilding = false; | ||
callback( new Error( 'JavaScript script failed' ) ); | ||
return; | ||
} | ||
|
||
console.log( 'Running eslint...' ); | ||
|
||
// Run eslint --fix on the generated file | ||
const eslintScript = spawn( | ||
'npx', | ||
[ | ||
'eslint', | ||
'--fix', | ||
'client/payment-methods/types.ts', | ||
], | ||
{ | ||
stdio: [ 'inherit', 'ignore', 'ignore' ], | ||
} | ||
); | ||
|
||
eslintScript.on( 'close', ( eslintCode ) => { | ||
isBuilding = false; | ||
if ( eslintCode !== 0 ) { | ||
console.error( 'Eslint failed' ); | ||
callback( new Error( 'Eslint failed' ) ); | ||
return; | ||
} | ||
|
||
// Clean up build artifacts | ||
removeDirectory( buildDir ); | ||
|
||
console.log( | ||
'Payment method definitions built successfully\n' | ||
); | ||
hasBuilt = true; | ||
callback(); | ||
} ); | ||
} ); | ||
} ); | ||
}; | ||
|
||
// Run build when files change during watch | ||
compiler.hooks.watchRun.tapAsync( | ||
'PaymentMethodsPlugin', | ||
( compilation, callback ) => { | ||
// Build on initial run or when files have changed | ||
if ( ! hasBuilt || compilation.modifiedFiles ) { | ||
// Only check for payment method changes if we have modified files | ||
if ( compilation.modifiedFiles ) { | ||
const modifiedFiles = Array.from( | ||
compilation.modifiedFiles || [] | ||
); | ||
|
||
// Ignore changes to generated files | ||
const hasPaymentMethodChanges = modifiedFiles.some( | ||
( file ) => | ||
file.includes( '/payment-methods/Configs/' ) && | ||
! file.includes( 'build/' ) && | ||
! file.includes( | ||
'client/payment-methods/types.ts' | ||
) | ||
); | ||
|
||
if ( ! hasPaymentMethodChanges ) { | ||
callback(); | ||
return; | ||
} | ||
|
||
console.log( | ||
'\nPayment method files changed, rebuilding...' | ||
); | ||
} | ||
|
||
hasBuilt = false; // Reset build flag | ||
buildPaymentMethods( callback ); | ||
} else { | ||
callback(); | ||
} | ||
} | ||
); | ||
} | ||
} | ||
|
||
module.exports = PaymentMethodsPlugin; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Significance: minor | ||
Type: dev | ||
|
||
Add centralized payment method definitions to streamline implementation and maintenance of payment methods. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/** | ||
* Internal dependencies | ||
*/ | ||
import type { PaymentMethodMapEntry } from '../payment-methods-map'; | ||
import { PaymentMethodDefinitions } from './types'; | ||
import { mapDefinitionToEntry } from './mapping'; | ||
|
||
/** | ||
* Generated payment method information using the backend-defined types | ||
*/ | ||
const GeneratedPaymentMethodInformationObject: Record< | ||
string, | ||
PaymentMethodMapEntry | ||
> = Object.fromEntries( | ||
Object.entries( PaymentMethodDefinitions ).map( ( [ key, def ] ) => [ | ||
key, | ||
mapDefinitionToEntry( def ), | ||
] ) | ||
); | ||
|
||
export default GeneratedPaymentMethodInformationObject; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './utils'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import * as React from 'react'; | ||
import classNames from 'classnames'; | ||
import type { ImgHTMLAttributes, FunctionComponent } from 'react'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import type { PaymentMethodDefinition } from '../types'; | ||
|
||
type ReactImgFuncComponent = FunctionComponent< | ||
ImgHTMLAttributes< HTMLImageElement > | ||
>; | ||
|
||
interface IconComponentOptions { | ||
hasBorder?: boolean; | ||
} | ||
|
||
/** | ||
* Creates an icon component from a given path and label | ||
* | ||
* @param iconPath - Path to the icon asset | ||
* @param label - Pre-translated text to use as alt text for the icon. | ||
* This should be already translated when passed to this function. | ||
* @param options - Additional options for the icon component | ||
*/ | ||
export const createIconComponent = ( | ||
iconPath: string, | ||
label: string, | ||
options: IconComponentOptions = { hasBorder: true } | ||
): ReactImgFuncComponent => { | ||
return ( { className, ...props } ): JSX.Element => ( | ||
<img | ||
className={ classNames( | ||
'payment-method__icon', | ||
options.hasBorder ? '' : 'no-border', | ||
className | ||
) } | ||
src={ iconPath } | ||
alt={ label } | ||
{ ...props } | ||
/> | ||
); | ||
}; | ||
|
||
/** | ||
* Creates an icon component from a payment method definition. | ||
* Uses the pre-translated title from the definition as the alt text. | ||
* Note: Payment method titles are translated in their PHP definition files. | ||
* | ||
* @param def - Payment method definition containing the icon path and pre-translated title | ||
* @param options - Additional options for the icon component | ||
*/ | ||
export const createPaymentMethodIconComponent = ( | ||
def: PaymentMethodDefinition, | ||
options?: IconComponentOptions | ||
): ReactImgFuncComponent => { | ||
return createIconComponent( def.settingsIcon, def.title, options ); | ||
}; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not exactly sure how this is compiling (maybe because it's just importing the TS type?), but there seem to be a circular dependency, here.
client/payment-methods/generated-map.ts
is importingclient/payment-methods-map.tsx
. Butclient/payment-methods-map.tsx
is also importingclient/payment-methods/generated-map.ts
.Maybe
PaymentMethodMapEntry
should be separated in a different file.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, good catch! Resolved this in a16c8f3