AgentStack
SKILL verified MIT Self-run

Salesforce D2c Setup

skill-alexkwitko-salesforce-agentforce-salesforce-d2c-setup · by alexkwitko

Use when setting up, configuring, or troubleshooting a Salesforce D2C (or B2B) Commerce store — WebStore/catalog/categories/pricebooks, product images (the CMS multipart upload), the two cart-calculation frameworks (legacy sfdc_checkout vs CartExtension/Cart Calculate), payment + shipping + tax integrations (mock or real), promotions + coupons, inventory, buyer groups + entitlement (incl. Commerc…

No reviews yet
0 installs
12 views
0.0% view→install

Install

$ agentstack add skill-alexkwitko-salesforce-agentforce-salesforce-d2c-setup

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README — it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-alexkwitko-salesforce-agentforce-salesforce-d2c-setup)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
13d ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming — see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps — measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Salesforce D2c Setup? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Salesforce D2C Commerce Setup Playbook

Practical, DX-first playbook for standing up a Salesforce D2C (or B2B) Commerce store end to end. Drawn from a real build (a coffee-equipment D2C store, "Bean & Brew"); the patterns apply to any catalog. Substitute your own products/brand.

> Companion reference: [reference/commerce-api-and-data-model.md](reference/commerce-api-and-data-model.md) has the exhaustive catalog — every Connect REST endpoint, the full cart/order/promotions object model, both calc-framework interface tables, the commerce/* LWC adapter list, the B2B-vs-D2C matrix, and the payment-framework objects. Load it when you need exact paths/signatures. This file is the workflow; that file is the catalog. > > Scope note: this is Commerce on the core Salesforce platform (Experience Cloud LWR + the Commerce data model), NOT "B2C Commerce Cloud" (SFRA / PWA Kit / Demandware) — a separate product. The official guide is the B2B & D2C Commerce Developer Guide.

#0 rule: Official-first for COMMERCE PLUMBING; custom LWC for DISTINCTIVE DESIGN

Keep the standard commerce components for the transactional path (PDP, cart, checkout, search) — never re-implement cart/checkout/pricing/tax; use the official mechanisms (Commerce store, OrderDeliveryMethod, CommercePayments, entitlement policies, Guest Buyer Profile, CartExtension calculators). But the OOTB Commerce LWR template's marketing pages (home/landing) are a generic placeholder ("Alpine Energy" banners, footer links all to /) — for a professional, on-brand 2026 storefront you SHOULD build custom LWR design components for the homepage/editorial sections. That's not "Frankenstein," it's the supported path (reference §17). Rule of thumb: custom design components for storytelling/merchandising pages; standard components for the buy-flow.

#1 rule: DX/CLI/API first, UI last

Order for every task:

  1. sf CLIsf project deploy/retrieve, sf data, sf apex run.
  2. Connect/REST API via CLIsf api request rest "/services/data/v62.0/..." (uses the stored session even though the raw token is hidden).
  3. Metadata XML deploy (mdapi when source-format type inference fails).
  4. Apex self-callout (for APIs with no sf api path, e.g. multipart uploads).
  5. Browser/Setup UI — last resort. To drive the org UI headlessly without a password, use sf org open --path "" --url-only → navigate the browser to that frontdoor URL (auto-authenticates with the CLI token). sf org display redacts the access token (returns a 54-char placeholder), so raw curl -H "Authorization: Bearer ..." 401s — use sf api request rest or the Apex session instead.

#2 rule: pick the calc framework up front (one or the other, never both)

Cart/checkout calculations run on one of two mutually-exclusive frameworks — they cannot be mixed:

  • Legacy "Checkout Integrations" — Apex implements sfdc_checkout.* interfaces, registered via RegisteredExternalService + StoreIntegratedService. Fast to mock; what older stores use.
  • Cart Extensions / Cart Calculate (current GA, Salesforce-recommended) — Apex extends CartExtension.* classes registered against extension points (e.g. Commerce_Domain_Tax_CartCalculator). Required for the Cart Calculate API and finer control.

Default to legacy only for a quick mock/demo or a store already on it. Choose CartExtension for new builds. Migrating between them is a rebuild of every calculator, not a flag — decide early. (Full interface/extension-point tables + signatures in the reference file §1.) Payment is outside both — always commercepayments (§4).


1. Store + data model (the spine)

Core objects: WebStore, ProductCatalog, ProductCategory, ProductCategoryProduct (product→category junction), Product2, PricebookEntry (standard + the store's pricebook → WebStorePricebook), BuyerGroup, CommerceEntitlementPolicy, CommerceEntitlementBuyerGroup (policy↔group), WebStoreBuyerGroup (store↔group), WebStoreCatalog (store↔catalog).

  • The standard "Commerce Store (LWR)" template in some orgs is B2B-only (no separate D2C template); the D2C distinction is buyer access + guest, not a different template. Create the store from the standard template either way.
  • Products need a PricebookEntry in both the standard pricebook and the store pricebook to be sellable. (8 products × 2 = 16 entries in the reference build.)
  • Add products to a category via ProductCategoryProduct(ProductCategoryId, ProductId); rebuild the search index afterward (see §8).
  • Product2 images are NOT a URL field — see §3.

2. Catalog distinct from other stores

If one org hosts multiple brands/stores, keep catalogs distinct: separate Product2 SKUs per brand, add only the brand's products to that store's category, and (if a category was shared) remove the other brand's ProductCategoryProduct rows. Use a Brand__c picklist on Order/Web_Event__c as a dimension (not a separate identity) when you want one unified customer across brands.

3. Product images — the hard part (CMS multipart upload)

ProductMedia (the image link) requires ProductId + ElectronicMediaId (→ ManagedContent, required) + ElectronicMediaGroupId. Gotchas that cost the most time:

  • ManagedContent is NOT DML-createable; ManagedContentVersion is not an SObject. Images must be created through the Connect CMS API.
  • The CMS source.ref will NOT accept a ContentDocument/ContentVersion Id ("This image reference isn't available").
  • The standard media groups already exist per store: query ElectronicMediaGroup — you'll find Tile Image, Product List Image, Product Detail Images (+ Banner, Attachments). Reuse them; ElectronicMediaGroup is not createable.
  • CMS workspace = ManagedContentSpace (query SELECT Id, Name FROM ManagedContentSpace).

Winning create recipe (POST /connect/cms/contents, multipart):

  • JSON part named content: {"contentSpaceOrFolderId":"","title":"...","urlName":"img-...","contentType":"sfdc_cms__image","contentBody":{"sfdc_cms:media":{"source":{"type":"file"}}}}
  • source.type must be the constant "file"; do NOT include ref and do NOT include sfdc_cms:altText (both rejected with additionalProperties errors).
  • Binary part named exactly contentData (the API demands this name regardless of ref), with filename + Content-Type: image/png.
  • Order matters: content part FIRST, then the contentData binary part.

How to send multipart headlessly (sf api request rest can't do multipart; the token is redacted): build the whole multipart body in Python, base64 it, and POST from Apex self-callout:

  • Add a RemoteSiteSetting for the org My-Domain (deploy as mdapi — source-format .remoteSiteSetting-meta.xml throws TypeInferenceError; use a package.xml + mdapi dir).
  • Apex: req.setEndpoint('https://.my.salesforce.com/services/data/v62.0/connect/cms/contents'); req.setHeader('Authorization','Bearer '+UserInfo.getSessionId()); req.setHeader('Content-Type','multipart/form-data; boundary=...'); req.setBodyAsBlob(EncodingUtil.base64Decode(b64));
  • Anonymous-apex script cap ≈ 1MB → upload one image per run (a batch of 8 base64 bodies = 207KB and still failed "Script too large" when combined with maps; loop one at a time, ~25KB each).
  • Generate images with Pillow (available locally) — clean branded PNGs render fine.

Then link + publish:

  • ProductMedia IS DML-createable: insert new ProductMedia(ProductId=p, ElectronicMediaId=managedContentId, ElectronicMediaGroupId=, SortOrder=n) — one per group (List/Detail/Tile = 3 per product).
  • Publish the content: POST /connect/cms/contents/publish body {"contentIds":[...]} (field is contentIds, not managedContentIds/ids). Returns a deploymentId / #DEPLOYED.
  • Verify per product via GET /services/data/v62.0/commerce/webstores/{webStoreId}/products/{productId}defaultImage.url populated + mediaGroups. The product search API (POST .../search/product-search) is what the LIST page reads — confirm it returns defaultImage too.
  • Direct media URLs require the storefront session; a bare-domain curl 301→404s to the wrong site — that's expected, not a defect.

4. Payment (mock for demo, real otherwise)

(Payment uses the commercepayments framework regardless of calc framework — request types, objects, client vs server flow in reference §8.)

  • Mock: an Apex CommercePayments.PaymentGatewayAdapter that always approves (real adapter branches on ctx.getPaymentRequestType(): Tokenize/Authorize/Capture/Refund). PaymentGatewayProvider can't be inserted via Apex DML — create with sf data create record. Then PaymentGateway + a NamedCredential.
  • Wire to the store: StoreIntegratedService(StoreId, ServiceProviderType='Payment', Integration=). Verify: SELECT ServiceProviderType, Integration FROM StoreIntegratedService WHERE StoreId='...'.

5. Shipping

  • OrderDeliveryMethod (e.g. "Standard Shipping", IsActive=true). The standard checkout can use a flat delivery method without a custom Apex shipping service. For dynamic rates, register a shipping integration like tax (§6).

6. Tax (mock = a real store-completeness win)

(This is the legacy sfdc_checkout path — see rule #2. On a CartExtension store, extend CartExtension.TaxCartCalculator against Commerce_Domain_Tax_CartCalculator instead; reference §1.)

  • Apex class BeanBrewMockTax implements sfdc_checkout.CartTaxCalculations { IntegrationStatus startCartProcessAsync(IntegrationInfo, Id cartId) {...} } — compute e.g. 8% per CartItem, write CartTax rows.
  • Gotcha: sfdc_checkout.IntegrationStatus has no message field — set only .status (SUCCESS/FAILED); log errors via System.debug.
  • Register: RegisteredExternalService(DeveloperName, MasterLabel, ExternalServiceProviderType='Tax', ExternalServiceProviderId=) then StoreIntegratedService(StoreId, ServiceProviderType='Tax', Integration=). Both ARE Apex-DML-creatable. StoreIntegratedService.ServiceProviderType enum: Flow, Price, Promotions, Inventory, Shipment, Tax, Payment, Extension.

7. Inventory / supply chain

  • ProductInventory/OCI is a separate (heavy, often UI-gated) enablement. Without it, products sell as available by default — fine for demos.
  • For Woo-style stock parity, seed Product2.Stock_On_Hand__c (this field often already exists from a Woo sync). It's a data layer, not a hard checkout block unless OCI is on.

8. Search index

  • Rebuild after catalog/category/image/entitlement changes: POST /services/data/v62.0/commerce/management/webstores/{id}/search/indexes body {}. Poll GET the same endpoint until indexStatus != "InProgress" (≈2–5 min). The LIST page reads the index; PDP reads live.

9. Buyers + entitlement (authenticated)

  • A buyer = AccountBuyerAccount(BuyerId, BuyerStatus='Active', IsActive=true)BuyerGroupMember(BuyerGroupId, BuyerId). BuyerStatus defaults to Pending — must be Active or carts fail "Invalid effective accountId."
  • D2C buyers are Person Accounts.
  • The store's buyer group must have an active CommerceEntitlementPolicy (CanViewProduct/CanViewPrice=true) linked via CommerceEntitlementBuyerGroup, and the group linked to the store via WebStoreBuyerGroup. The note in Setup: "each assigned buyer group must have at least one active entitlement."
  • ⚠️ An entitlement policy with CanViewProduct=true but ZERO CommerceEntitlementProduct rows entitles NOTHING — there is no "grant all products" boolean field; the storefront returns 404 on every product (guest AND buyer) until you insert CommerceEntitlementProduct(PolicyId, ProductId) rows for the products (or category rows). This is the #1 "catalog is empty for shoppers but admin API sees products" cause.
  • REST cart as internal admin doesn't work: POST /commerce/webstores/{id}/carts?effectiveAccountId=X returns "Invalid effective accountId" even for a valid Active buyer — D2C checkout runs in the shopper's storefront session (guest or community user), not an admin REST context. To prove checkout, log in as a real buyer (recipe below).

Provisioning a test BUYER LOGIN (to prove checkout headlessly-ish via the live storefront)

A buyer storefront login needs ALL of these — missing any one yields 403/404 on the commerce APIs:

  1. Portal User on the Person Account (ContactId = PersonContactId, profile = the store's CspLitePortal "Shopper Profile"). ⚠️ The portal account's OWNER must have a UserRole ("portal account owner must have a role") — assign one first. Set the password in a SEPARATE transaction from the insert (System.setPassword right after insert silently doesn't take for login).
  2. D2C Commerce Shopper permission-set LICENSE (PermissionSetLicense DeveloperName CommerceShopperPsg) assigned via PermissionSetLicenseAssign — without a commerce PSL the APIs 403.
  3. The standard CommerceUser ("Shopper") permission set (grants the commerce system perm) + a custom permset with Read on ProductCatalog/ProductCategory/ProductCategoryProduct/Product2/ProductMedia (the Customer Community license rejects Create WebCart — cart objects are handled by the commerce runtime, so don't add cart CRUD).
  4. The Account = Active BuyerAccount + BuyerGroupMember, and the policy has CommerceEntitlementProduct rows (above).
  5. Re-login after any permset/PSL change — community sessions cache permissions; the storefront keeps 403ing on the old session until you log out/in.

10. Guest browsing + guest checkout (the saga)

  1. Site public access: in the site's DigitalExperienceBundle, sfdc_cms__site//content.jsoncontentBody.authenticationType must be AUTHENTICATED_WITH_PUBLIC_ACCESS_ENABLED (valid enum; NOT AUTHENTICATED_AND_ANONYMOUS). Deploy (mdapi) + sf community publish --name "".
  2. WebStore flags (Apex updateable): OptionsGuestBrowsingEnabled, OptionsGuestCartEnabled, OptionsGuestCheckoutEnabled, OptionsPreserveGuestCartEnabled; GuestBuyerProfileId (object prefix 3K0, only Id/Name queryable).
  3. CRITICAL — run the official Guest Access automation in Setup UI: Commerce app → Store → Settings → Store → Buyer Access tab → Guest Access → Disable → Enable → Continue. This is the step that assigns object permissions to the guest profile, creates the Guest Buyer Profile, sets person-account default, and sets Experience-Builder public access. Setting the WebStore flags via Apex does NOT run this automation — the UI banner literally says "Click Disable and then Enable to run the full automation." There is no headless API for the guest entitlement/permission assignment.
  4. Confirm Store Access → Buyer Groups = your buyer group (re-assigning errors as a duplicate = it's wired).
  5. Rebuild the search index; guest catalog visibility can lag (entitlement + index recalc; minutes to hours in a Dev Edition org). Even with a complete, correct setup the guest grid may stay empty for a while — verify in an authenticated session meanwhile.
  • Drive all the Setup-UI steps headlessly via the sf org open --url-only frontdoor (no password).

Guest visibility = THREE independent gates (all must pass) — the authoritative checklist

A guest 404 on the product API and 403 on pricing almost always means one of these is missing:

  1. Commerce entitlement — the policy on the store's buyer group needs CommerceEntitlementProduct rows (no "all products" flag; 0 rows ⇒ 404 for everyone). Guests inherit the store buyer group via WebStoreBuyerGroup; the Manage Guest Access automation associates the Guest Buyer Profile with the buyer group (the guest buyer profile is NOT a BuyerGroupMember row — that association is internal, which is why

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.