Claude Skill

wc-checkout-block-payment-method

Build or audit a WooCommerce Checkout Block payment-method integration. Covers the separate PHP `WC_Payment_Gateway`, Blocks `AbstractPaymentMethodType`, JavaScript `registerPaymentMethod`, stable gateway identifiers, `onPaymentSetup`, Store API `payment_data`, saved-token UI, le

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download lonsdale201-wp-agent-skills-woocommerce_wc-checkout-block-payment-method-52f6020.zip · 7 KB
Part of lonsdale201/wp-agent-skills — 226 skills

Install

skills CLI npx skills add https://github.com/Lonsdale201/wp-agent-skills/tree/main/woocommerce/wc-checkout-block-payment-method
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install lonsdale201-wp-agent-skills@llmmart
Git git clone https://github.com/Lonsdale201/wp-agent-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole lonsdale201/wp-agent-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

WooCommerce Checkout Block payment methods

A Checkout Block integration is an adapter around a payment gateway, not a replacement for it. Implement and test each layer deliberately.

Keep the four layers separate

Layer Responsibility
WC_Payment_Gateway Settings, availability, validation, server-side provider calls, refunds, and classic checkout
AbstractPaymentMethodType Registers Block assets and exposes non-secret settings to JavaScript
registerPaymentMethod() Renders the Block UI, reports availability, prepares opaque payment data, and handles client SDK events
Store API processing Bridges payment_data to process_payment() or an explicit PaymentContext/PaymentResult handler

Store API payment requirements only filter eligible methods. They do not register a payment UI or process money.

Use one stable identifier

Make these values equal unless a verified compatibility requirement says otherwise:

WC_Payment_Gateway::$id
AbstractPaymentMethodType::$name
registerPaymentMethod({ name })
registerPaymentMethod({ paymentMethodId })

paymentMethodId is what Checkout sends as payment_method and uses to find the PHP gateway. It defaults to name; set it explicitly if the client registration name differs. A provider payment-method type, wallet type, and Woo gateway ID are different identifiers and must not be conflated.

Register the PHP Blocks adapter

Register after woocommerce_blocks_loaded and guard the Blocks class:

use Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType;
use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry;

final class MyPlugin_Blocks_Payment_Method extends AbstractPaymentMethodType {
    protected $name = 'myplugin_gateway';

    public function initialize(): void {
        $this->settings = get_option( 'woocommerce_myplugin_gateway_settings', array() );
    }

    public function is_active(): bool {
        return 'yes' === $this->get_setting( 'enabled', 'no' );
    }

    public function get_payment_method_script_handles(): array {
        $asset = file_exists( MYPLUGIN_PATH . 'build/checkout.asset.php' )
            ? require MYPLUGIN_PATH . 'build/checkout.asset.php'
            : array(
                'dependencies' => array( 'wc-blocks-registry', 'wc-settings', 'wp-element', 'wp-html-entities' ),
                'version'      => MYPLUGIN_VERSION,
            );

        wp_register_script(
            'myplugin-checkout-block',
            MYPLUGIN_URL . 'build/checkout.js',
            $asset['dependencies'],
            $asset['version'],
            true
        );
        wp_set_script_translations( 'myplugin-checkout-block', 'myplugin' );

        return array( 'myplugin-checkout-block' );
    }

    public function get_payment_method_data(): array {
        return array(
            'title'       => $this->get_setting( 'title', __( 'Pay securely', 'myplugin' ) ),
            'description' => $this->get_setting( 'description', '' ),
            'supports'    => $this->get_supported_features(),
        );
    }
}

add_action( 'woocommerce_blocks_loaded', static function (): void {
    if ( ! class_exists( AbstractPaymentMethodType::class ) ) {
        return;
    }

    add_action(
        'woocommerce_blocks_payment_method_type_registration',
        static function ( PaymentMethodRegistry $registry ): void {
            $registry->register( new MyPlugin_Blocks_Payment_Method() );
        }
    );
} );

Use the generated *.asset.php dependency/version file. Do not expose secret keys, webhook secrets, unrestricted client secrets, internal errors, or full provider configuration through get_payment_method_data().

Register the client method

Read PHP data from {name}_data. Render a real interactive component in content and a safe preview in edit.

const { registerPaymentMethod } = window.wc.wcBlocksRegistry;
const { getPaymentMethodData } = window.wc.wcSettings;
const { createElement, useEffect } = window.wp.element;
const { decodeEntities } = window.wp.htmlEntities;

const settings = getPaymentMethodData( 'myplugin_gateway', {} );

const Content = ( { eventRegistration, emitResponse } ) => {
	const { onPaymentSetup } = eventRegistration;

	useEffect( () => {
		const unsubscribe = onPaymentSetup( async () => {
			try {
				const reference = await collectOpaqueProviderReference();
				return {
					type: emitResponse.responseTypes.SUCCESS,
					meta: {
						paymentMethodData: {
							myplugin_payment_reference: reference.id,
						},
					},
				};
			} catch ( error ) {
				return {
					type: emitResponse.responseTypes.ERROR,
					message: 'Please check your payment details and try again.',
				};
			}
		} );

		return unsubscribe;
	}, [ onPaymentSetup, emitResponse.responseTypes.SUCCESS, emitResponse.responseTypes.ERROR ] );

	return createElement( 'div', null, decodeEntities( settings.description || '' ) );
};

registerPaymentMethod( {
	name: 'myplugin_gateway',
	paymentMethodId: 'myplugin_gateway',
	label: decodeEntities( settings.title || 'Pay securely' ),
	ariaLabel: decodeEntities( settings.title || 'Pay securely' ),
	content: createElement( Content ),
	edit: createElement( 'div', null, decodeEntities( settings.description || '' ) ),
	canMakePayment: () => true,
	supports: {
		features: settings.supports || [ 'products' ],
		showSavedCards: false,
		showSaveOption: false,
	},
} );

Replace the placeholder collector with the provider's hosted field/SDK. Card or bank credentials must go directly to the provider; send WordPress only an opaque reference, confirmation token, or local Woo token ID. Use onPaymentSetup; onPaymentProcessing is deprecated. Always return the unsubscribe function so rerenders do not duplicate observers or charges.

Choose one server processing path

Reuse process_payment()

For a conventional gateway, return paymentMethodData from onPaymentSetup. Checkout POSTs it to /wc/store/v1/checkout; Woo sanitizes keys, converts values to strings, temporarily copies them to $_POST, and calls the selected gateway's validate_fields() and process_payment().

Use lowercase snake-case keys because Store API applies sanitize_key(). Treat every value as untrusted. For structured data, encode deliberately and validate size, schema, and types after decoding.

Handle Store API explicitly

Use woocommerce_rest_checkout_process_payment_with_context when the legacy bridge cannot express the provider flow:

add_action(
    'woocommerce_rest_checkout_process_payment_with_context',
    static function ( $context, $result ): void {
        if ( 'myplugin_gateway' !== $context->payment_method ) {
            return;
        }

        // Validate $context->payment_data and process the authoritative order.
        $result->set_status( 'success' );
        $result->set_redirect_url( $context->order->get_checkout_order_received_url() );
    },
    10,
    2
);

Once this handler sets a result status, Woo skips the legacy gateway bridge. Do not charge once here and again in process_payment().

Client confirmation and saved tokens

  • Use onPaymentSetup to validate or create an opaque provider-side reference before Checkout sends the order.
  • If the server must first return a scoped client secret, put only required scalar data in PaymentResult::payment_details, then complete the SDK step through onCheckoutSuccess. Woo 11.0.0 casts each payment-detail value to string. Settle the order from verified server state/webhooks.
  • savedTokenComponent receives a local Woo token ID, not the provider credential. Resolve it server-side and verify ownership, gateway, type, expiry/state, and provider customer.
  • Set supports.showSavedCards and supports.showSaveOption only if the PHP gateway implements safe tokenization. These flags are UI capability claims, not security checks.
  • Keep canMakePayment cheap, deterministic, and side-effect free. It can run repeatedly and asynchronously; memoize costly provider capability checks.

Audit and test matrix

  1. Test both shortcode checkout and Checkout Block; they are independent paths.
  2. Test block editor preview separately from shopper checkout. Do not mount live provider Elements in editor mode.
  3. Test new method, saved token, save checkbox, guest/account policy, zero total, redirect/SCA, asynchronous settlement, decline, retry, refresh, and back-button paths.
  4. Change cart totals/shipping while the payment UI is mounted and verify stale provider intents are updated or replaced safely.
  5. Double-click Place Order and replay the request; provider idempotency must prevent duplicate money movement.
  6. Verify no PAN/CVV, secret, client secret, Cart-Token, raw provider response, or sensitive billing payload reaches logs or general settings data.
  7. Treat canMakePayment and client validation as UX only; repeat amount, currency, ownership, eligibility, and state checks on the server.

Cross-references

  • wc-stripe-future-payments for charge-now/save-for-later and off-session Stripe flows.
  • wc-stripe-link-payments for Link-specific token shapes and consent.
  • See references/blocks-payment-lifecycle.md for the full request lifecycle, integration choices, and review checklist.

References

Files (wp-agent-skills)
  • agents
    • openai.yaml 260 B
      interface:
        display_name: "WooCommerce Checkout Block Payments"
        short_description: "Build Checkout Block payment integrations"
        default_prompt: "Use $wc-checkout-block-payment-method to implement or audit a WooCommerce Checkout Block payment integration."
      
  • references
    • blocks-payment-lifecycle.md 6.6 KB
      # Checkout Block payment lifecycle reference
      
      Load this reference when implementing a new Block gateway, integrating a provider SDK, or diagnosing duplicate/missing payment processing.
      
      ## End-to-end sequence
      
      ```text
      woocommerce_blocks_loaded
        -> register AbstractPaymentMethodType
        -> enqueue registered JS handle
        -> expose {integration_name}_data
        -> JavaScript registerPaymentMethod()
        -> shopper selects method
        -> onPaymentSetup observers validate/prepare opaque data
        -> POST /wc/store/v1/checkout
             payment_method = paymentMethodId
             payment_data[] = key/value pairs
        -> PaymentContext(payment_method, order, string payment_data)
        -> woocommerce_rest_checkout_process_payment_with_context
             explicit handler, or
             priority-999 legacy bridge -> validate_fields() -> process_payment()
        -> PaymentResult(status, redirect_url, string payment_details)
        -> onCheckoutSuccess for any required client SDK completion
        -> verified provider webhook/reconciliation finalizes asynchronous state
      ```
      
      ## Registration contracts
      
      ### PHP adapter
      
      `AbstractPaymentMethodType` provides these extension points:
      
      - `$name` / `get_name()` identifies the Blocks integration and the `{name}_data` settings object.
      - `initialize()` loads dependencies and settings.
      - `is_active()` determines whether scripts/data are registered.
      - `get_payment_method_script_handles()` registers frontend scripts.
      - `get_payment_method_script_handles_for_admin()` may supply a lighter editor bundle.
      - `get_payment_method_data()` exposes client settings.
      - `get_supported_features()` declares compatibility requirements.
      
      The adapter is not a `WC_Payment_Gateway`; it must not become a second source of payment settings or state.
      
      ### JavaScript registration
      
      Required normal-payment properties are `name`, `label`, `ariaLabel`, `content`, `edit`, and `canMakePayment`. Relevant optional properties include:
      
      - `paymentMethodId`: server gateway ID; defaults to `name`.
      - `savedTokenComponent`: UI rendered for a selected saved Woo token.
      - `placeOrderButtonLabel`: label for the normal button.
      - `placeOrderButton`: full custom component; use only when the provider requires it.
      - `supports.features`: capabilities compared with cart payment requirements.
      - `supports.showSavedCards`: whether Woo token choices appear.
      - `supports.showSaveOption`: whether Woo's save-method checkbox appears.
      
      Do not provide both `placeOrderButton` and `placeOrderButtonLabel`. A custom button is not used when a saved token is selected, so the default path must still work.
      
      ## Event response shapes
      
      Register from a React effect and unsubscribe on unmount:
      
      ```js
      useEffect( () => {
      	const unsubscribe = onPaymentSetup( async () => ( {
      		type: emitResponse.responseTypes.SUCCESS,
      		meta: {
      			paymentMethodData: {
      				myplugin_reference: 'opaque-reference',
      			},
      		},
      	} ) );
      
      	return unsubscribe;
      }, [ onPaymentSetup, emitResponse.responseTypes.SUCCESS ] );
      ```
      
      Use:
      
      - `SUCCESS` when payment input/preparation is valid and checkout may reach the server.
      - `ERROR` for invalid shopper/payment input and optionally `validationErrors`.
      - `FAILURE` for a payment attempt that failed; show only a safe customer message.
      
      The object belongs below `meta.paymentMethodData`, not at the top level. Store API accepts key/value entries, applies `sanitize_key()` to names and `wc_clean()` to values, then `PaymentContext::set_payment_data()` casts values to strings. Prefer short opaque scalar values.
      
      `PaymentResult::set_payment_details()` also casts every value to string in WooCommerce 11.0.0. Return short scalar values such as a scoped client secret or provider intent ID; do not expect nested response objects to survive.
      
      `onPaymentProcessing` may still appear in older examples but is deprecated. Use `onPaymentSetup`.
      
      ## Pick the processing choreography
      
      ### Opaque reference before order processing
      
      The provider JS creates a token/reference during `onPaymentSetup`; Store API sends it to the PHP gateway, which performs the authoritative server request. This is the simplest fit for `process_payment()`.
      
      ### Server intent, then client confirmation
      
      The Store API handler creates/updates an intent using the persisted order amount and returns a narrowly scoped client secret in `payment_details`. `onCheckoutSuccess` confirms or handles the next action. Webhooks/reconciliation own final asynchronous success.
      
      Protect this design against:
      
      - cart/order amount changes between element mount and server processing;
      - duplicate checkout requests and repeated event observers;
      - navigating away during client confirmation;
      - provider success with a lost browser response;
      - `requires_action`, redirect, `processing`, decline, and timeout states.
      
      ### Provider-hosted redirect
      
      The PHP gateway returns a provider session URL in the redirect result. Verify the return and webhook independently; a browser return alone is not proof of payment.
      
      ## Saved-token boundary
      
      The token prop supplied to `savedTokenComponent` is the local database token ID. On the server:
      
      1. Load with `WC_Payment_Tokens::get()`.
      2. Match `get_user_id()` to the authenticated customer.
      3. Match `get_gateway_id()` and expected token subclass/type.
      4. Resolve the provider identifier and compare its remote Customer/account.
      5. Reject deleted, detached, unsupported, or unusable methods.
      
      Never accept a provider PaymentMethod ID from a browser as proof that the shopper owns it.
      
      ## Server handler rules
      
      An explicit `woocommerce_rest_checkout_process_payment_with_context` callback must:
      
      - return immediately for every other `payment_method`;
      - validate all `payment_data` again;
      - use the context order's amount and currency, not client values;
      - use provider idempotency tied to the operation/order/attempt;
      - set a valid result status: `success`, `failure`, `pending`, or `error`;
      - set only customer-safe payment details and redirects;
      - throw a customer-safe exception on failure while logging redacted diagnostics.
      
      Setting any result status prevents Woo's priority-999 legacy bridge from invoking `process_payment()`. Use one owner for a charge.
      
      ## Review traps
      
      - A classic-checkout screenshot does not prove Block support.
      - `woocommerce_store_api_register_payment_requirements()` is eligibility only.
      - `canMakePayment()` is repeatedly evaluated UX logic, not authorization.
      - PHP settings data is visible to every checkout visitor.
      - Editor rendering is not a live checkout and must not create provider objects.
      - React rerenders can remount fields or add observers; cleanup and provider-instance ownership must be explicit.
      - A successful client SDK call is not a substitute for signed webhooks and idempotent settlement.
      - A provider's reusable wallet/token may not map to `WC_Payment_Token_CC`; preserve polymorphism.
      
  • SKILL.md 10.9 KB
    ---
    name: wc-checkout-block-payment-method
    description: Build or audit a WooCommerce Checkout Block payment-method integration. Covers the separate PHP `WC_Payment_Gateway`, Blocks `AbstractPaymentMethodType`, JavaScript `registerPaymentMethod`, stable gateway identifiers, `onPaymentSetup`, Store API `payment_data`, saved-token UI, legacy `process_payment()` bridging, advanced `PaymentContext`/`PaymentResult` processing, SDK confirmation, security, performance, and classic-versus-Block tests. Use when a gateway works in shortcode checkout but is missing or broken in Checkout Block, or when adding card fields, wallets, tokenization, redirects, or custom payment data to Blocks.
    metadata:
      wp-skills-author: "Soczó Kristóf"
      wp-skills-contact: "mailto:lonsdale201@hotmail.com"
      wp-skills-plugin: "woocommerce"
      wp-skills-plugin-version-tested: "11.0.0"
      wp-skills-php-min: "7.4"
      wp-skills-last-updated: "2026-08-05"
    ---
    
    # WooCommerce Checkout Block payment methods
    
    A Checkout Block integration is an adapter around a payment gateway, not a replacement for it. Implement and test each layer deliberately.
    
    ## Keep the four layers separate
    
    | Layer | Responsibility |
    |---|---|
    | `WC_Payment_Gateway` | Settings, availability, validation, server-side provider calls, refunds, and classic checkout |
    | `AbstractPaymentMethodType` | Registers Block assets and exposes non-secret settings to JavaScript |
    | `registerPaymentMethod()` | Renders the Block UI, reports availability, prepares opaque payment data, and handles client SDK events |
    | Store API processing | Bridges `payment_data` to `process_payment()` or an explicit `PaymentContext`/`PaymentResult` handler |
    
    Store API payment requirements only filter eligible methods. They do not register a payment UI or process money.
    
    ## Use one stable identifier
    
    Make these values equal unless a verified compatibility requirement says otherwise:
    
    ```text
    WC_Payment_Gateway::$id
    AbstractPaymentMethodType::$name
    registerPaymentMethod({ name })
    registerPaymentMethod({ paymentMethodId })
    ```
    
    `paymentMethodId` is what Checkout sends as `payment_method` and uses to find the PHP gateway. It defaults to `name`; set it explicitly if the client registration name differs. A provider payment-method type, wallet type, and Woo gateway ID are different identifiers and must not be conflated.
    
    ## Register the PHP Blocks adapter
    
    Register after `woocommerce_blocks_loaded` and guard the Blocks class:
    
    ```php
    use Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType;
    use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry;
    
    final class MyPlugin_Blocks_Payment_Method extends AbstractPaymentMethodType {
        protected $name = 'myplugin_gateway';
    
        public function initialize(): void {
            $this->settings = get_option( 'woocommerce_myplugin_gateway_settings', array() );
        }
    
        public function is_active(): bool {
            return 'yes' === $this->get_setting( 'enabled', 'no' );
        }
    
        public function get_payment_method_script_handles(): array {
            $asset = file_exists( MYPLUGIN_PATH . 'build/checkout.asset.php' )
                ? require MYPLUGIN_PATH . 'build/checkout.asset.php'
                : array(
                    'dependencies' => array( 'wc-blocks-registry', 'wc-settings', 'wp-element', 'wp-html-entities' ),
                    'version'      => MYPLUGIN_VERSION,
                );
    
            wp_register_script(
                'myplugin-checkout-block',
                MYPLUGIN_URL . 'build/checkout.js',
                $asset['dependencies'],
                $asset['version'],
                true
            );
            wp_set_script_translations( 'myplugin-checkout-block', 'myplugin' );
    
            return array( 'myplugin-checkout-block' );
        }
    
        public function get_payment_method_data(): array {
            return array(
                'title'       => $this->get_setting( 'title', __( 'Pay securely', 'myplugin' ) ),
                'description' => $this->get_setting( 'description', '' ),
                'supports'    => $this->get_supported_features(),
            );
        }
    }
    
    add_action( 'woocommerce_blocks_loaded', static function (): void {
        if ( ! class_exists( AbstractPaymentMethodType::class ) ) {
            return;
        }
    
        add_action(
            'woocommerce_blocks_payment_method_type_registration',
            static function ( PaymentMethodRegistry $registry ): void {
                $registry->register( new MyPlugin_Blocks_Payment_Method() );
            }
        );
    } );
    ```
    
    Use the generated `*.asset.php` dependency/version file. Do not expose secret keys, webhook secrets, unrestricted client secrets, internal errors, or full provider configuration through `get_payment_method_data()`.
    
    ## Register the client method
    
    Read PHP data from `{name}_data`. Render a real interactive component in `content` and a safe preview in `edit`.
    
    ```js
    const { registerPaymentMethod } = window.wc.wcBlocksRegistry;
    const { getPaymentMethodData } = window.wc.wcSettings;
    const { createElement, useEffect } = window.wp.element;
    const { decodeEntities } = window.wp.htmlEntities;
    
    const settings = getPaymentMethodData( 'myplugin_gateway', {} );
    
    const Content = ( { eventRegistration, emitResponse } ) => {
    	const { onPaymentSetup } = eventRegistration;
    
    	useEffect( () => {
    		const unsubscribe = onPaymentSetup( async () => {
    			try {
    				const reference = await collectOpaqueProviderReference();
    				return {
    					type: emitResponse.responseTypes.SUCCESS,
    					meta: {
    						paymentMethodData: {
    							myplugin_payment_reference: reference.id,
    						},
    					},
    				};
    			} catch ( error ) {
    				return {
    					type: emitResponse.responseTypes.ERROR,
    					message: 'Please check your payment details and try again.',
    				};
    			}
    		} );
    
    		return unsubscribe;
    	}, [ onPaymentSetup, emitResponse.responseTypes.SUCCESS, emitResponse.responseTypes.ERROR ] );
    
    	return createElement( 'div', null, decodeEntities( settings.description || '' ) );
    };
    
    registerPaymentMethod( {
    	name: 'myplugin_gateway',
    	paymentMethodId: 'myplugin_gateway',
    	label: decodeEntities( settings.title || 'Pay securely' ),
    	ariaLabel: decodeEntities( settings.title || 'Pay securely' ),
    	content: createElement( Content ),
    	edit: createElement( 'div', null, decodeEntities( settings.description || '' ) ),
    	canMakePayment: () => true,
    	supports: {
    		features: settings.supports || [ 'products' ],
    		showSavedCards: false,
    		showSaveOption: false,
    	},
    } );
    ```
    
    Replace the placeholder collector with the provider's hosted field/SDK. Card or bank credentials must go directly to the provider; send WordPress only an opaque reference, confirmation token, or local Woo token ID. Use `onPaymentSetup`; `onPaymentProcessing` is deprecated. Always return the unsubscribe function so rerenders do not duplicate observers or charges.
    
    ## Choose one server processing path
    
    ### Reuse `process_payment()`
    
    For a conventional gateway, return `paymentMethodData` from `onPaymentSetup`. Checkout POSTs it to `/wc/store/v1/checkout`; Woo sanitizes keys, converts values to strings, temporarily copies them to `$_POST`, and calls the selected gateway's `validate_fields()` and `process_payment()`.
    
    Use lowercase snake-case keys because Store API applies `sanitize_key()`. Treat every value as untrusted. For structured data, encode deliberately and validate size, schema, and types after decoding.
    
    ### Handle Store API explicitly
    
    Use `woocommerce_rest_checkout_process_payment_with_context` when the legacy bridge cannot express the provider flow:
    
    ```php
    add_action(
        'woocommerce_rest_checkout_process_payment_with_context',
        static function ( $context, $result ): void {
            if ( 'myplugin_gateway' !== $context->payment_method ) {
                return;
            }
    
            // Validate $context->payment_data and process the authoritative order.
            $result->set_status( 'success' );
            $result->set_redirect_url( $context->order->get_checkout_order_received_url() );
        },
        10,
        2
    );
    ```
    
    Once this handler sets a result status, Woo skips the legacy gateway bridge. Do not charge once here and again in `process_payment()`.
    
    ## Client confirmation and saved tokens
    
    - Use `onPaymentSetup` to validate or create an opaque provider-side reference before Checkout sends the order.
    - If the server must first return a scoped client secret, put only required scalar data in `PaymentResult::payment_details`, then complete the SDK step through `onCheckoutSuccess`. Woo 11.0.0 casts each payment-detail value to string. Settle the order from verified server state/webhooks.
    - `savedTokenComponent` receives a local Woo token ID, not the provider credential. Resolve it server-side and verify ownership, gateway, type, expiry/state, and provider customer.
    - Set `supports.showSavedCards` and `supports.showSaveOption` only if the PHP gateway implements safe tokenization. These flags are UI capability claims, not security checks.
    - Keep `canMakePayment` cheap, deterministic, and side-effect free. It can run repeatedly and asynchronously; memoize costly provider capability checks.
    
    ## Audit and test matrix
    
    1. Test both shortcode checkout and Checkout Block; they are independent paths.
    2. Test block editor preview separately from shopper checkout. Do not mount live provider Elements in editor mode.
    3. Test new method, saved token, save checkbox, guest/account policy, zero total, redirect/SCA, asynchronous settlement, decline, retry, refresh, and back-button paths.
    4. Change cart totals/shipping while the payment UI is mounted and verify stale provider intents are updated or replaced safely.
    5. Double-click Place Order and replay the request; provider idempotency must prevent duplicate money movement.
    6. Verify no PAN/CVV, secret, client secret, Cart-Token, raw provider response, or sensitive billing payload reaches logs or general settings data.
    7. Treat `canMakePayment` and client validation as UX only; repeat amount, currency, ownership, eligibility, and state checks on the server.
    
    ## Cross-references
    
    - `wc-stripe-future-payments` for charge-now/save-for-later and off-session Stripe flows.
    - `wc-stripe-link-payments` for Link-specific token shapes and consent.
    - See [references/blocks-payment-lifecycle.md](references/blocks-payment-lifecycle.md) for the full request lifecycle, integration choices, and review checklist.
    
    ## References
    
    - Official payment-method integration: <https://developer.woocommerce.com/docs/block-development/extensible-blocks/cart-and-checkout-blocks/checkout-payment-methods/payment-method-integration>
    - Official checkout events: <https://developer.woocommerce.com/docs/block-development/extensible-blocks/cart-and-checkout-blocks/checkout-payment-methods/checkout-flow-and-events/>
    - Verified source paths:
      - `wp-content/plugins/woocommerce/src/Blocks/Payments/Integrations/AbstractPaymentMethodType.php`
      - `wp-content/plugins/woocommerce/src/Blocks/Payments/PaymentMethodRegistry.php`
      - `wp-content/plugins/woocommerce/src/StoreApi/Payments/PaymentContext.php`
      - `wp-content/plugins/woocommerce/src/StoreApi/Payments/PaymentResult.php`
      - `wp-content/plugins/woocommerce/src/StoreApi/Legacy.php`
      - `wp-content/plugins/woocommerce/src/StoreApi/Utilities/CheckoutTrait.php`
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related