Skip to content

fix(vue-starter-template): validate checkout and restore guest orders - #2724

Open
Patryk Tomczyk (patzick) wants to merge 9 commits into
mainfrom
cursor/fix-starter-checkout-guest-validation-8ee9
Open

Patryk Tomczyk (patzick) wants to merge 9 commits into
mainfrom
cursor/fix-starter-checkout-guest-validation-8ee9

Conversation

@patzick

@patzick Patryk Tomczyk (patzick) commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Description

Checkout in vue-starter-template sent the register/order request even when required fields like email were never touched, guest checkout could not complete from the UI, and clicking Create customer account had no way back to guest mode.

This change:

  • Validates email and billing address on Confirm and place order even if the fields were never focused, and shows the errors on the fields instead of calling the Store API
  • Keeps guest checkout as the default (guest: true, empty password) and adds a Continue as guest control after the password field appears
  • Requires zip code (and state when the selected country has states) before registration
  • Removes the extra Next button from one-page checkout. Confirm and place order creates the guest session (POST /account/register with guest: true, which does not create an account) and then places the order (POST /checkout/order)
  • Resolves storefrontUrl from the current sales channel domains when the configured devStorefrontUrl is not listed, so guest register can succeed against the public demo API and checkout can reach POST /checkout/order
  • Points devStorefrontUrl at https://frontends-starter-template.vercel.app as the preferred storefront URL

Relates to the guest/account toggle issues around #2683 and the checkout flow from #2700.

Type of change

Bug fix (non-breaking change that fixes an issue)

ToDo's

  • Changeset file provided
  • Documentation added/updated
  • Unit-Tests added/updated
  • E2E-Tests added/updated
  • Related Issue updated

Screenshots (if applicable)

Empty form, Confirm and place order (no field focused): client-side required errors, including email.

Untouched checkout fields show required errors

Create customer account reveals password and a way back:

Create account shows password and Continue as guest

Continue as guest restores guest mode:

Continue as guest hides the password field

Walkthrough recording:

checkout_untouched_validation_and_guest_toggle.mp4

Additional context

Verified with the Store API: POST /checkout/order without a customer returns FRAMEWORK__ROUTING_CUSTOMER_NOT_LOGGED_IN. Guest checkout must call POST /account/register with guest: true first, then POST /checkout/order.

The public demo sales channel currently lists only https://demo-frontends.shopware.store/figma. Registering with the starter Vercel host as storefrontUrl is rejected (VIOLATION::NO_SUCH_CHOICE_ERROR), which is why place-order previously never reached /checkout/order. getStorefrontUrl() now falls back to a domain from the session context in that case.

Open in Web Open in Cursor 

Show client-side errors on submit even when fields were never focused,
keep guest checkout as the default with a way back from create-account,
and require zip/state before the register request is sent.
@vercel

vercel Bot commented Sep 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Actions Updated
frontends-starter-template-extended Skipped Skipped Sep 16, 2026 4:39pm UTC
frontends-vue-starter-template Skipped Skipped Sep 16, 2026 4:39pm UTC

Request Review

Point registration storefrontUrl at https://frontends-starter-template.vercel.app instead of the deprecated demo-store domain.
@vercel
vercel Bot temporarily deployed to Preview – old-frontends-demo September 16, 2026 10:27 Inactive
@cursor

cursor Bot commented Sep 16, 2026

Copy link
Copy Markdown

Browser walkthrough of the starter checkout fixes (untouched-field validation, create-account / continue-as-guest toggle, filled guest form).

Empty form after Next — required errors on untouched fields, including email:

Untouched checkout fields show required errors

Create customer account shows the password field and Continue as guest:

Create account shows password and Continue as guest

Continue as guest hides the password field again:

Continue as guest hides the password field

Recording:

checkout_untouched_validation_and_guest_toggle.mp4

Add the browser verification images and compressed recording so they can be shown inline on the pull request.
@cursor

cursor Bot commented Sep 16, 2026

Copy link
Copy Markdown

Walkthrough screenshots and recording, hosted on this PR so they render inline:

Empty form after Next — required errors on untouched fields, including email:

Untouched checkout fields show required errors

Create customer account shows the password field and Continue as guest:

Create account shows password and Continue as guest

Continue as guest hides the password field again:

Continue as guest hides the password field

Recording:

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Security review

No medium, high, or critical vulnerabilities in this change.

Reviewed the starter checkout validation/guest-toggle work and the devStorefrontUrl update:

  • Registration still goes to Store API POST /account/register. Guest mode sends guest: true and an empty password; account mode sends the password only when the shopper opts in. Client $validate() is UX — the backend remains the auth boundary.
  • Place order without a session only runs register and does not call createOrder.
  • Validation and API error text are Vue-interpolated (FormInputField, LayoutNotification); no v-html on these paths.
  • devStorefrontUrl is a compile-time Shopware demo origin, not attacker-controlled; Shopware allowlists sales-channel domains for storefrontUrl.
  • No dependency or lockfile changes, and no secret logging.

No prior security-review threads to re-report. Slack summary was skipped (posting tool not configured).

Open in Web View Automation 

Sent by Cursor Automation: Review pull requests for exploitable security issues and flag only validated findings before merge

Screenshots and the recording are attached on the pull request as
GitHub user-attachments so they render inline, instead of living in
the repository or as external links.
@cursor

cursor Bot commented Sep 16, 2026

Copy link
Copy Markdown

Walkthrough screenshots and recording, attached on this issue so they render inline:

Empty form after Next — required errors on untouched fields, including email:

Untouched checkout fields show required errors

Create customer account shows the password field and Continue as guest:

Create account shows password and Continue as guest

Continue as guest hides the password field again:

Continue as guest hides the password field

Recording:

checkout_untouched_validation_and_guest_toggle.mp4

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Security review

No medium, high, or critical vulnerabilities in this change.

Re-reviewed d58f2ff after the latest synchronize. The only delta since the previous assessment is removing walkthrough PNG/MP4 files from the repo (they now live on GitHub issue attachments). Checkout code is unchanged.

  • Registration still goes to Store API POST /account/register. Guest mode sends guest: true and an empty password; account mode sends the password only when the shopper opts in. Client $validate() is UX — the backend remains the auth boundary.
  • Place order without a session only runs register and does not call createOrder.
  • Validation and API error text are Vue-interpolated (FormInputField, LayoutNotification); no v-html on these paths.
  • devStorefrontUrl is a compile-time Shopware demo origin, not attacker-controlled; Shopware allowlists sales-channel domains for storefrontUrl.
  • No dependency or lockfile changes, and no secret logging.

No prior security-review finding threads to re-report. Slack summary was skipped (posting tool not configured).

Open in Web View Automation 

Sent by Cursor Automation: Review pull requests for exploitable security issues and flag only validated findings before merge

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Security review

No medium, high, or critical vulnerabilities in this change.

Re-reviewed head d58f2ffd (walkthrough media removed from .github/pr-assets/; checkout/register code unchanged from the prior assessment):

  • Registration still goes to Store API POST /account/register. Guest mode sends guest: true and an empty password; account mode sends the password only when the shopper opts in. Client $validate() is UX — the backend remains the auth boundary.
  • Place order without a session only runs register and does not call createOrder.
  • Validation and API error text are Vue-interpolated (FormInputField, LayoutNotification); no v-html on these paths.
  • devStorefrontUrl is a compile-time Shopware demo origin, not attacker-controlled; Shopware allowlists sales-channel domains for storefrontUrl.
  • No dependency or lockfile changes, and no secret logging.

No prior security-review finding threads to re-report. Slack summary was skipped (posting tool not configured).

Open in Web View Automation 

Sent by Cursor Automation: Review pull requests for exploitable security issues and flag only validated findings before merge

Remove the extra Next button from one-page checkout. Confirm and
place order now creates the guest session (Store API register with
guest: true) and then calls checkout/order, instead of stopping after
register.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Security review

No medium, high, or critical vulnerabilities in this change.

Re-reviewed head 2353ba8b after the latest synchronize. The new delta combines guest/account register and place order into one Confirm action (saveCustomerDetails() → Store API POST /account/register, then createOrder()POST /checkout/order). That restores intended guest checkout; it is not an authz bypass. createOrder still needs a Store API customer context, which register establishes, and canPlaceOrder plus the backend still require shipping/payment methods.

  • Registration still goes to Store API POST /account/register. Guest mode sends guest: true and an empty password; account mode sends the password only when the shopper opts in. Client $validate() is UX — the backend remains the auth boundary.
  • Validation and API error text are Vue-interpolated (FormInputField, LayoutNotification); no v-html on these paths.
  • devStorefrontUrl is a compile-time Shopware demo origin, not attacker-controlled; Shopware allowlists sales-channel domains for storefrontUrl.
  • No dependency or lockfile changes, and no secret logging.

No prior security-review finding threads to re-report. Slack summary was skipped (posting tool not configured).

Open in Web View Automation 

Sent by Cursor Automation: Review pull requests for exploitable security issues and flag only validated findings before merge

Guest checkout still has to POST /account/register with guest: true
before POST /checkout/order. Register was 400ing because
devStorefrontUrl was not a Sales Channel domain, so place order never
reached createOrder. getStorefrontUrl() now falls back to a domain
from the current session context when the preferred URL is not listed.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Security review

No medium, high, or critical vulnerabilities in this change.

Re-reviewed head a522cbba after the latest synchronize. The new delta is getStorefrontUrl() falling back to a sales-channel domain from session context when devStorefrontUrl / window.location.origin is not listed under Sales Channel → Domains.

  • That value is only sent as storefrontUrl on Store API POST /account/register and newsletter subscribe. The backend still allowlists it against configured domains, so a client-side fallback cannot point confirmation/recovery links at an arbitrary host.
  • Combined guest register then place order remains the intended Store API flow (guest: true then POST /checkout/order). createOrder still needs a customer context; canPlaceOrder plus the backend still require shipping/payment.
  • Client $validate() is UX. Guest mode sends guest: true and an empty password; account mode sends the password only when the shopper opts in.
  • Validation and API error text are Vue-interpolated (FormInputField, Notification); no v-html on these paths.
  • devStorefrontUrl is a compile-time Shopware demo origin. No dependency or lockfile changes, and no secret logging.

No prior security-review finding threads to re-report. Slack summary was skipped (posting tool not configured).

Open in Web View Automation 

Sent by Cursor Automation: Review pull requests for exploitable security issues and flag only validated findings before merge

Validate both customer form groups and refresh shipping/payment
without failing the whole place-order flow if one promise rejects.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Security review

No medium, high, or critical vulnerabilities in this change.

Re-reviewed head 21772b02 after the latest synchronize. The new delta switches checkout validation and post-register shipping/payment refresh to Promise.allSettled.

  • $validate() is fail-closed: a rejected validation is treated as invalid and blocks place-order.
  • Shipping/payment refresh failures no longer abort saveCustomerDetails. Place-order is still gated by canPlaceOrder (selected shipping and payment) and Store API POST /checkout/order.
  • Combined guest register then place order remains the intended Store API flow (guest: true then POST /checkout/order). createOrder still needs a customer context.
  • getStorefrontUrl() is only sent as storefrontUrl on register/newsletter subscribe. The backend still allowlists it against configured sales-channel domains.
  • Client $validate() is UX. Guest mode sends guest: true and an empty password; account mode sends the password only when the shopper opts in.
  • Validation and API error text are Vue-interpolated (FormInputField, Notification); no v-html on these paths.
  • devStorefrontUrl is a compile-time Shopware demo origin. No dependency or lockfile changes, and no secret logging.

No prior security-review finding threads to re-report. Slack summary was skipped (posting tool not configured).

Open in Web View Automation 

Sent by Cursor Automation: Review pull requests for exploitable security issues and flag only validated findings before merge

@vercel
vercel Bot temporarily deployed to Preview – frontends-vue-starter-template September 16, 2026 16:39 Inactive
@vercel
vercel Bot temporarily deployed to Preview – old-frontends-demo September 16, 2026 16:39 Inactive
@vercel
vercel Bot temporarily deployed to Preview – frontends-starter-template-extended September 16, 2026 16:39 Inactive

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Security review

No medium, high, or critical vulnerabilities in this change.

Re-reviewed head ab3aaca9 after ready_for_review. The only new commit since the last review is a merge of main (Cart recipe docs already on the base). Checkout validation, guest register + place order, and getStorefrontUrl() fallback are unchanged.

  • Combined guest register then place order is the intended Store API flow (guest: true then POST /checkout/order). createOrder still needs a customer context; canPlaceOrder plus the backend still require shipping/payment.
  • $validate() via Promise.allSettled is fail-closed. Shipping/payment refresh failures do not skip those gates.
  • getStorefrontUrl() is only sent as storefrontUrl on register/newsletter subscribe. The backend still allowlists it against configured sales-channel domains.
  • Client $validate() is UX. Guest mode sends guest: true and an empty password; account mode sends the password only when the shopper opts in.
  • Validation and API error text are Vue-interpolated (FormInputField, Notification); no v-html on these paths.
  • devStorefrontUrl is a compile-time Shopware demo origin. No dependency or lockfile changes, and no secret logging.

No prior security-review finding threads to re-report. Slack summary was skipped (posting tool not configured).

Open in Web View Automation 

Sent by Cursor Automation: Review pull requests for exploitable security issues and flag only validated findings before merge

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security review

No medium, high, or critical vulnerabilities in this change.

Re-reviewed head ab3aaca9 after the latest synchronize. The only delta since 21772b02 is a merge of main (Cart recipe docs from #2722). Checkout, register, and getStorefrontUrl are unchanged.

  • $validate() remains fail-closed: a rejected validation is treated as invalid and blocks place-order.
  • Shipping/payment refresh failures still do not abort saveCustomerDetails. Place-order is gated by canPlaceOrder (selected shipping and payment) and Store API POST /checkout/order.
  • Combined guest register then place order remains the intended Store API flow (guest: true then POST /checkout/order). createOrder still needs a customer context.
  • getStorefrontUrl() is only sent as storefrontUrl on register/newsletter subscribe. Session domains come from GET /context; the backend still allowlists the value against configured sales-channel domains.
  • Client $validate() is UX. Guest mode sends guest: true and an empty password; account mode sends the password only when the shopper opts in.
  • Validation and API error text are Vue-interpolated (FormInputField, Notification); no v-html on these paths.
  • devStorefrontUrl is a compile-time Shopware demo origin. No dependency or lockfile changes, and no secret logging.

No prior security-review finding threads to re-report. Slack summary was skipped (posting tool not configured).

Open in Web View Automation 

Sent by Cursor Automation: Review pull requests for exploitable security issues and flag only validated findings before merge

},
acceptedDataProtection: true,
});
await Promise.allSettled([getShippingMethods(), getPaymentMethods()]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have to add forceReload. Without this option data are not refreshed

let customerReady = isUserSession.value;
try {
if (!customerReady) {
await saveCustomerDetails();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the place order fails, this function will block address editing,

useUser -> register

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain/framework For x-team workflows and tracking efforts

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants