Problem/Motivation
When a merchant adds a new Accept Hosted credit card payment via the order "Add payment" form (entity.commerce_payment.add_form, introduced by #3538210), any billing profile field beyond the standard address components is silently discarded — for example a custom tax_number field added to the customer profile bundle.
Root cause:
1. AcceptHosted::getAcceptHostedCheckoutForm() generates the Authorize.net token using whichever billing profile is passed in $contexts['billing_profile'] (on this form, the profile entity from the billing_information inline form — see PaymentMethodAddForm::createContexts()), but never attaches that profile to the order.
2. Accept Hosted hides Drupal's own submit control and drives the actual transaction through a client-side redirect straight into an iframe pointed at Authorize.net's hosted page (commerce_authnet.accept_hosted.form.js). Authorize.net's own hosted page redirects the browser to the merchant-return route once the transaction completes. Drupal's form-submit handler for this form is never invoked for this gateway.
3. When onReturn() runs (in a separate HTTP request, since the browser has fully navigated), AcceptHosted::createPaymentMethodFromPayment() finds no billing profile on the order and creates a brand-new one, populated only from the fields Authorize.net's transaction response echoes back in billTo (country, zip, city, address, state, firstName, lastName, company). Any other field configured on the site's profile bundle is never populated, because it was never part of Authorize.net's request/response payload to begin with.
This does not affect normal checkout, because there the order's billing profile is already fully populated (via the checkout flow's own billing information step) before getAcceptHostedCheckoutForm() ever runs, and createPaymentMethodFromPayment() only patches the address field on top of it — nothing else is ever touched or lost.
I also found the identical code pattern (context billing profile never attached to the order, same address-only reconstruction from the remote response) in commerce_cybersource's UnifiedCheckout gateway, though that module doesn't yet support this "Add payment" admin form, so it hasn't surfaced there in practice.
Steps to reproduce
1. Add a custom field to the customer profile bundle (e.g. a "Tax number" text field) that is exposed on the billing profile form.
2. Configure an order to use the Authorize.net Accept Hosted payment gateway.
3. As an administrator, go to the order's Add Payment form, choose "New credit/debit card", enter a new billing address including the custom field, click the address "Save" button (added by #3586506), then complete the payment inside the Accept Hosted iframe.
4. Inspect the resulting payment method's/order's billing profile: the address is present (it matches whatever Authorize.net echoed back), but the custom field is empty.
Proposed resolution
Since the billing profile must not be attached to the order until the payment is actually confirmed (nothing should be written to the order on a build/rebuild that might never complete), stage the profile entered on the Add Payment form in a PrivateTempStore keyed by order ID (in PaymentMethodAddForm::createContexts()) instead of attaching it to the order early. Then, in createPaymentMethodFromPayment(), when the order has no billing profile yet, fall back to the tempstore-staged profile as the base before applying the address fields Authorize.net's response confirms, and clear the tempstore entry once consumed.
Related issues
- #3538210: Support iframe / JS based payment methods on the Add payment form (introduces this code path)
- #3586506: Add a "Save" button for new address entry and refresh the payment method add form when it's submitted (introduces the address-entry flow this bug depends on)
Issue fork commerce_authnet-3624298
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
vmarchukComment #4
vmarchukComment #5
vmarchukComment #7
vmarchukCommitted!