• Resolved Matt Pramschufer

    (@mattpramschufer)


    I’m experiencing an issue with manually created orders on the pay-for-order page where payment methods are created without billing details, causing payments to be blocked by Stripe Radar rules.

    Environment:

    • WooCommerce: Latest version
    • WooCommerce Stripe Gateway: 10.3.1
    • WordPress: Latest version

    The Issue:

    When an admin manually creates an order and sends a customer the pay-for-order link (/checkout/order-pay/{order_id}/?pay_for_order=true&key={order_key}), the payment fails due to a Stripe Radar rule that blocks payments with missing billing address:

    Block if :address_line1_check: in ('fail', 'not_provided')
    

    The Stripe API shows the payment method is created with address_line1_check: ‘not_provided’, causing the Radar rule to block the payment.

    Root Cause:

    1. The pay-for-order page doesn’t display billing address fields like the normal checkout page does
    2. Admins enter a billing address when creating the order, but customers may use a credit card with a different billing address
    3. The Stripe payment element creates the payment method on the frontend without billing details
    4. By the time the payment method reaches the server, it has already been evaluated by Stripe Radar and blocked

    Solutions Attempted:

    Over 3+ hours of debugging, I attempted the following server-side solutions:

    1. Added custom billing fields to pay-for-order page using woocommerce_pay_order_before_payment hook – Successfully displayed fields and captured customer input
    2. Tried multiple filters to inject billing details:
      • wc_stripe_generate_payment_request – Discovered billing_details not valid on payment intent
      • wc_stripe_generate_create_intent_request – Failed because filter fires during confirmation, not creation; Stripe returned “Received unknown parameter: customer” on confirm endpoint
      • wc_stripe_payment_intent_data – Filter never fired during pay-for-order flow
    3. Attempted to update payment method after creation:
      • Attached payment method to Stripe customer
      • Updated payment method billing_details via Stripe API
      • Failed because Radar rule evaluates before backend code runs
    4. Updated order billing address using woocommerce_before_pay_action hook – Successfully saved to order but doesn’t affect payment method creation

    The Core Problem:

    The payment method is created by JavaScript on the frontend without billing details before any server-side code can intervene. There’s no server-side hook that fires early enough in the pay-for-order flow to inject billing details before Stripe creates and evaluates the payment method.

    What’s Needed:

    Either:

    1. Display billing address fields on pay-for-order page (like normal checkout) so customers can verify/update billing address to match their credit card
    2. Pass billing details to Stripe Elements when creating payment method on pay-for-order page so address_line1_check is properly evaluated
    3. Add a filter/hook that fires before payment method creation where billing details can be configured for the Stripe Elements instance

    Comparison:

    Normal checkout flow: Shows billing fields → collects data → creates payment method with billing details → Stripe Radar evaluates with address

    Pay-for-order flow: No billing fields → creates payment method without billing details → Stripe Radar blocks for missing address

    Request:

    Can you either:

    • Add billing address fields to the pay-for-order page template
    • Modify the Stripe Elements initialization to include billing details from the order
    • Provide documentation on the correct filter/hook to use for this scenario

    This affects any WooCommerce store using Stripe Radar rules that require billing address verification for manually created orders.

    Logs/Evidence:

    The Stripe API request shows payment methods being created without billing_details, resulting in address_line1_check: ‘not_provided’ which triggers Radar blocks.

    Thank you for your help with this issue. Happy to provide additional details or test any solutions.

Viewing 12 replies - 1 through 12 (of 12 total)
  • Plugin Support Frank Remmy (woo-hc)

    (@frankremmy)

    Hi @mattpramschufer,

    Thanks for the detailed report. I ran a full reproduction test on my end, and the behavior looks normal. Here’s a short screen recording of the successful test for reference: https://streamable.com/ku41fl

    To help narrow down the behaviour, here are a few focused checks they can run:

    • Confirm the manual order actually contains a full billing address before sending the pay‑for‑order link. Missing fields lead to Stripe receiving billing_details: null.
    • Temporarily switch to Storefront to rule out theme overrides that might remove or filter billing fields on the pay‑for‑order template.
    • Disable any checkout‑modifying plugins (checkout field editors, address validators, multi‑step checkouts) to ensure nothing is interfering with how WooCommerce loads billing data. Do the same for other plugins except WooCommerce and Stripe.
    • Check for custom code snippets that hook into checkout or pay‑for‑order actions, especially anything touching billing fields or the PaymentIntent request.
    • Make sure the Stripe Payment Element is enabled and not being altered by custom JavaScript that might override billing details.
    • Create a fresh manual order with complete billing information and test again to rule out malformed data on older orders.
    • Review browser console logs on the pay‑for‑order page to see whether Stripe Elements is throwing any warnings about missing or invalid billing fields.

    Once they’ve gone through these checks and it keeps happening, it would be helpful if you could share your system status report so we can review the full environment details. You can find this under WooCommerce, Status, then copy the report and share it via https://pastebin.comhttps://quickforget.com, or https://gist.github.com.

    I hope that helps. Let us know if you need anything else.

    Thread Starter Matt Pramschufer

    (@mattpramschufer)

    Thanks for the quick response. I will investigate more, but this still poses a problem. This assumes that the admin user that is creating the order knows the correct billing address associated with the credit card the customer is going to use. Should there not be a way for the customer to update the billing address if needed in order to use a different credit card?

    Did you test when you add a Stripe Radar rule of

    Block if :address_line1_check: in ('fail', 'not_provided')

    Hi @mattpramschufer,

    You’re right, if the customer wants to use a different card, they need a way to provide billing details. On the pay-for-order page, the current Stripe Elements flow doesn’t show billing fields by default.

    A few things to try:

    • Add billing address fields to the pay-for-order template so customers can enter or update them.
    • Make sure the Stripe Payment Element is initialized with these billing details before creating the payment method.
    • Test with your Radar rule active to confirm the address is properly sent.

    This should let customers pay with a different card without triggering the Radar block.

    Thread Starter Matt Pramschufer

    (@mattpramschufer)

    Hi @saivutukuru,

    Thanks for confirming the issue exists. Unfortunately, what you’ve suggested is exactly what we already attempted during our 3+ hours of debugging, and it doesn’t work due to how Stripe Elements creates payment methods.

    What We Already Tried:

    1. Added billing address fields to pay-for-order template  – Done this was easy

    • Used woocommerce_pay_order_before_payment hook to display billing fields
    • Fields successfully render and capture customer input
    • Order billing address successfully updates via woocommerce_before_pay_action hook

    2. Attempted to initialize Stripe Payment Element with billing details  – Failed

    This is where we’re stuck. The problem is:

    • Stripe Elements creates the payment method on the frontend (JavaScript) before any server-side code runs
    • By the time our PHP code can access the billing details, the payment method has already been created without billing details
    • The Radar rule evaluates immediately when the payment method is created, so it’s already blocked before we can update it

    3. Tested with Radar rule active  Confirmed

    The specific Radar rule blocking payments is:

    Block if :address_line1_check: in ('fail', 'not_provided')
    

    The Stripe API shows payment methods being created with address_line1_check: 'not_provided', which triggers the block.

    The Technical Issue:

    We need to pass billing details to Stripe Elements JavaScript when it creates the payment method, but we cannot find:

    1. Where the Stripe Payment Element is initialized in the plugin’s JavaScript for the pay-for-order page
    2. What JavaScript hooks/filters exist to pass billing details from the order to Stripe Elements
    3. How to configure the Elements instance to collect billing details before calling stripe.createPaymentMethod()

    Server-side filters we tested (all failed):

    • wc_stripe_generate_payment_request – billing_details not valid on payment intent
    • wc_stripe_generate_create_intent_request – fires during confirmation (too late), Stripe rejects customer parameter on confirm endpoint
    • wc_stripe_payment_intent_data – never fires during pay-for-order flow

    Can you provide specific guidance on:

    1. Which JavaScript file initializes Stripe Elements on the pay-for-order page?
    2. What JavaScript hooks/filters can we use to pass the order’s billing details to stripe.createPaymentMethod()?
    3. Is there a proper way to extend the plugin’s JavaScript to include billing details, or does this require modifying core plugin files?

    The billing fields are successfully displayed and captured – we just need to know how to pass that data to Stripe Elements before it creates the payment method on the frontend.

    Thanks for your help!

    Plugin Support shahzeen(woo-hc)

    (@shahzeenfarooq)

    Hi there!

    Thank you for sharing all the details and clearly explaining your troubleshooting steps. I understand your concern and the challenges you’re facing with passing billing details to Stripe Elements on the pay-for-order page.

    To better understand the issue, I created a manual order with only the email address in the billing details and tried placing an order in Stripe test mode. Everything worked fine on my end, and the order was placed successfully. Could you please provide the exact steps to replicate the issue you’re experiencing so we can better verify it?

    Regarding your questions about customizing the plugin’s JavaScript or extending Stripe Elements to include billing details: please note that we do not provide support for customizations.

    If you need more in-depth support or want to consider professional assistance for customization, I can recommend WooExperts and Codeable.io as options for getting professional help. Alternatively, you can also ask your development questions in the  WooCommerce Community Slack as custom code falls outside our usual scope of support.

    Thread Starter Matt Pramschufer

    (@mattpramschufer)

    The critical step that you are missing is 3.) Tested with Radar rule active you need to add the Radar rule and then test

    Steps to Create a Custom Radar Rule:

    1. Access Rules: Log in to Stripe and navigate to Radar > Rules.
    2. Add Rule: Click “+ Add rule” to create a new rule for payments.
    3. Define Logic: Input the rule syntax.
      • Block missing address _1: :address_line1_check: in ('fail', 'not_provided')
    4. Action: Select the action: BlockReview, or Allow.
    5. Save: Click Save to activate the rule. 

    Once you have that rule in place, then if you create an order and then visit the appropriate /checkout/order-pay/{order_id}/?pay_for_order=true&key={order_key}) page, all credit cards will fail because it is not passing the address.

    Plugin Support shahzeen(woo-hc)

    (@shahzeenfarooq)

    Hi there!

    Thank you for the detailed explanation and for sharing the exact Radar rule and steps that’s very helpful. At the moment, the pay‑for‑order flow in our Stripe gateway focuses on collecting the minimum required fields to complete payment, and it does not mirror the full billing form used on the main checkout page. That means some Radar rules which depend on specific address checks (like address_line1_check) may not be fully compatible with pay‑for‑order payments.

    Resolving this would require custom development to modify how billing details are passed to Stripe on the pay-for-order page. As this falls outside the plugin’s default functionality, it would be considered a customization, and we’re unable to provide support for custom code changes.

    If you need more in-depth support or want to consider professional assistance for customization, I can recommend WooExperts and Codeable.io as options for getting professional help.

    Alternatively, you can also ask your development questions in the  WooCommerce Community Slack as custom code falls outside our usual scope of support.

    Also, I’d suggest submitting a feature request here:
    https://woocommerce.com/feature-requests/stripe/

    This will allow our developers to consider adding an option to collect complete billing details on the pay-for-order page in a future update.

    Thread Starter Matt Pramschufer

    (@mattpramschufer)

    I still do not think you are understanding here. Let me reframe it and approach it from a different angle for you.

    The plugin is not passing billing details to Stripe when creating payment methods on the pay-for-order page, which creates a security vulnerability.

    Security Risk:

    By omitting billing details from the payment method creation, the plugin enables card testing attacks. Fraudsters use stolen card numbers to make small test purchases to verify if cards are active. Without billing address verification:

    1. No AVS (Address Verification System) checks – Stripe cannot validate if the card’s billing address matches
    2. Radar rules that depend on address verification are bypassed – Rules like Block if :address_line1_check: in ('fail', 'not_provided') cannot function
    3. Card testing is easier – Attackers can rapidly test stolen cards without needing valid billing addresses
    4. Higher fraud rates – Merchants using manual orders are exposed to fraudulent transactions

    This is particularly problematic for manually created orders where admins may enter placeholder billing addresses, then send payment links to customers.

    Evidence:

    When payment methods are created on pay-for-order pages, Stripe shows address_line1_check: 'not_provided'. This proves that billing details are NOT being sent to Stripe when stripe.confirmPayment() or stripe.createPaymentMethod() is called.

    The Root Issue:

    From what I can see, since the JavaScript is minified, your JavaScript code is not using the customerBillingData (that you pass from PHP to JavaScript) when creating the Payment Element or confirming the payment.

    According to Stripe’s Payment Element documentation, billing details should be passed like this:

    const paymentElement = elements.create('payment', {
      defaultValues: {
        billingDetails: {
          name: 'John Doe',
          email: 'john@example.com',
          address: {
            line1: '123 Main St',
            city: 'New York',
            state: 'NY',
            postal_code: '10001',
            country: 'US'
          }
        }
      }
    });
    

    Your plugin is clearly NOT doing this, otherwise Stripe would receive the billing address and wouldn’t return address_line1_check: 'not_provided'.

    What Needs to be Fixed:

    1. JavaScript Bug (Security Issue):

    Your JavaScript needs to be updated to:

    • Use wc_stripe_upe_params.customerBillingData when creating the Payment Element with defaultValues.billingDetails
    • OR pass billing details in the confirmPayment() call

    This is a security vulnerability, not just a feature request. Without billing details, merchants cannot use Stripe’s fraud prevention tools effectively on pay-for-order pages.

    2. Wrong Data Source:

    The billing data being passed is from the logged-in user’s saved address (WC()->customer) instead of the order’s billing address that the admin entered. For pay-for-order pages, this needs to use the order’s billing data:

    In /includes/payment-methods/class-wc-stripe-upe-payment-gateway.php at lines 554-572:

    // Current code uses:
    $customer = WC()->customer;
    
    // Should be (for pay-for-order):
    if ( $is_pay_for_order ) {
        $order_id = absint( get_query_var( 'order-pay' ) );
        $order = wc_get_order( $order_id );
        
        if ( $order ) {
            $stripe_params['customerBillingData'] = [
                'name'    => trim( $order->get_billing_first_name() . ' ' . $order->get_billing_last_name() ),
                'email'   => $order->get_billing_email(),
                'phone'   => $order->get_billing_phone(),
                'address' => [
                    'country'     => $order->get_billing_country(),
                    'line1'       => $order->get_billing_address_1(),
                    'line2'       => $order->get_billing_address_2(),
                    'city'        => $order->get_billing_city(),
                    'state'       => $order->get_billing_state(),
                    'postal_code' => $order->get_billing_postcode(),
                ],
            ];
        }
    }
    

    3. Add Filter for Extensibility (WordPress Best Practice):

    Even after fixing #1 and #2, you should add a filter to allow developers to customize the billing data source. This follows WordPress plugin best practices and allows merchants to implement additional security measures.

    Add this filter after line 571:

    // Allow developers to customize billing data
    $stripe_params['customerBillingData'] = apply_filters( 
        'wc_stripe_upe_customer_billing_data', 
        $stripe_params['customerBillingData'], 
        $customer, 
        $is_pay_for_order,
        $order ?? null
    );
    

    This would allow developers to:

    • Add custom billing fields to the pay-for-order page for customers to verify/update
    • Capture updated billing information from the customer before payment
    • Override the billing data passed to Stripe Elements
    • Implement additional fraud prevention measures

    Without this filter, developers who want to let customers update their billing address on the pay-for-order page (to prevent the admin-entered address from being used with a different card) would have to fork your entire plugin.

    Impact:

    This affects:

    1. Any merchant using Stripe Radar rules that validate billing addresses (standard fraud prevention)
    2. Any merchant creating manual orders where the customer’s card billing address differs from the order billing address
    3. Security-conscious merchants trying to prevent card testing attacks
    4. Merchants in high-fraud industries who rely on AVS checks

    Summary:

    1. Fix the JavaScript to actually send billing details to Stripe (security fix)
    2. Use order billing data instead of customer saved data for pay-for-order (bug fix)
    3. Add a PHP filter to allow developers to customize the billing data source (WordPress best practice)

    Please prioritize this as a security issue. The current implementation weakens fraud prevention on pay-for-order pages.

    Plugin Support shahzeen(woo-hc)

    (@shahzeenfarooq)

    Hi there!

    Thank you for providing more information that’s very helpful.

    I’ve internally escalated this issue to our development team for further review. I’ll be sure to update you as soon as I hear back from them.

    Thanks again for taking the time to share these details.

    Hi @mattpramschufer,

    Thanks again for your detailed breakdown and for helping us narrow this down.

    We were able to reproduce the issue internally and have created a GitHub issue to track this with our development team:

    https://github.com/woocommerce/woocommerce-gateway-stripe/issues/5030

    You can follow that thread for updates, progress, and any technical notes as the team investigates further.

    We really appreciate you surfacing this, especially with the Radar rule context. It helped a lot in confirming the root cause.

    Thread Starter Matt Pramschufer

    (@mattpramschufer)

    Thank you! @saivutukuru I will follow progress on the GitHub issue going forward.

    Thanks, @mattpramschufer.

    Please keep us posted here once it’s implemented so others can benefit from the update as well.

    If you’ve found our support helpful, please leave us a review here.

Viewing 12 replies - 1 through 12 (of 12 total)

You must be logged in to reply to this topic.