Shopify
Connect and manage your Shopify store from the Control Centre card.
Last updated:
Shopify
Source-only payment evidence protection
The 12 October implementation keeps original paid receipts first-write immutable and atomic with Sales effects: exact replay is a no-op; contradictory reuse is refused. Restrictive tenant-safe database references prevent parent deletion from erasing captures/receipts or clearing actor attribution. This is isolated synthetic verification only, not a reopened Sandbox run or live activation. Live processing remains disabled.
Local disconnection and retained records
Disconnecting Shopify locally clears its saved credentials and pending connection authorisation, disables inventory reads/writes and marks the Connection disconnected. It keeps the same shop binding and local Sales, Stock, Listings and payment evidence. Reconnecting uses that original shop; it does not replace the Connection with a different store.
This does not uninstall the Shopify app, revoke access at Shopify or erase customer data. The authenticated request-path implementation is verified only with synthetic records on the isolated full schema; no live activation is included.
Where to access it
- Open Control Centre.
- Find the Shopify card.
- Choose the sliders icon.
What you can do
- Manage to open Shopify connection controls.
- From Manage, choose Connect to Shopify to link your store.
- From Manage, choose Reconnect Shopify to re-authorise an existing store connection.
- From Manage, choose Disconnect Shopify to clear ChannelWeave's saved authorisation while keeping the original store binding and local records.
Authorisation update: deployment pending
An update to renewable Shopify authorisation has been implemented locally but has not yet been deployed or verified with an existing store. Existing stores will need to approve access again once the update is available; this work has not changed their current Shopify authorisation.
The updated connection renews access when needed. If approval has expired or Shopify rejects renewal, use Reconnect Shopify for the same original store. A different store cannot replace that store's retained records.
Disconnect does not delete Sales or other saved records, uninstall the app in Shopify, or cancel access already issued to an operation that is in progress.
The connection form requires JavaScript. Enter your .myshopify.com domain (or
store name), then choose Connect to Shopify or press Enter. ChannelWeave
validates the store before redirecting you to Shopify for approval. If
JavaScript is disabled, enable it and reload the page; Cancel remains
available.
Privacy requests: preparation in progress
A privacy-request receipt and review foundation has been implemented locally, but is not yet available as a completed customer service. Receiving a request will not itself export or delete information. Local checks now distinguish a verified store identity from a missing or conflicting one; they do not establish the identity of a buyer or permission to erase records. Operational retention periods have been approved, and local previews now demonstrate proposed export, removal and review outcomes using fictional test records only. Merchant-specific accounting requirements, retained fields and safe processing still need to be confirmed before activation. These previews do not export, delete or change existing customer information. Disconnecting your store is not a request to erase its saved records.
The synthetic previews also flag missing Sales/fulfilment field examples and unresolved data-processing safeguards. These are preparation checks, not proof that every copy of a customer's information has been found or processed.
A fuller fictional example now exercises nested data copies and independent delivery evidence without displaying their contents or changing processing permissions. Live privacy processing remains disabled.
Fictional before/after comparisons also highlight changes to selected delivery and dispatch evidence. Matching those examples does not approve deletion or confirm that every customer-data copy has been covered.
Synthetic route tests confirmed a remaining gap: newer fulfilment events can restore removed tracking copies. Rejecting an old duplicate alone does not establish safe privacy processing; live processing remains disabled while those safeguards and policies are unresolved.
A fictional contract model now exercises proposed tracking safeguards for concurrent operations, reconnects and expiry. Follow-up tests in a separate temporary database verify the proposed locking and rollback design using fictional records. They do not change live ingestion or prove application-level enforcement, complete removal or approved retention. Live privacy processing remains disabled.
Further fictional fulfilment tests now check that selected tracking fields can be removed without losing delivery evidence or weakening dispatch protections. Unrecognised data blocks that test transformation rather than being silently discarded. Whole order/payment payloads and live enforcement remain unresolved; these checks do not enable deletion or complete privacy processing.
Order and payment preparation also distinguishes complete saved payloads from selected refund evidence. These copies have different replay and transaction boundaries, so a successful webhook is not proof that every copy is covered. Whole-order deletion remains blocked and live privacy processing remains off.
A further fictional model places order, line and payment copies, an event receipt and an audit entry behind one customer/store/order transaction fence. Its tests exercise all-or-nothing writes, duplicate delivery and suppression races. This is a test-only design, not a repair to live ingestion or a policy for retaining financial or raw payload fields. Live processing remains disabled.
Synthetic replay preparation now distinguishes event identity from delivery identity and keeps the original body fingerprint separate from source times. Exact matching evidence can be classified as a duplicate; a different event for the same order requires reconciliation even if its timestamp looks newer. These classifications do not authorise ingestion or change existing webhook behaviour.
A test-only composition now checks these identity decisions alongside the symbolic paid-copy fence: duplicates and conflicts leave copies untouched; candidate writes publish their fictional evidence together or not at all. Suppression, reconnect and expiry checks remain in force. This is an in-memory test, not proof of database-wide replay protection: namespace-wide identity history is a separate locking concern from the per-order fence. Live processing remains disabled.
A further synthetic contract separates immutable event evidence from delivery aliases and requires a customer/store/app/topic lock before the order lock. A new alias must not rewrite order copies or audit; reconnect and restored-history checks retain the same scope. These test-only safeguards do not enable live privacy processing or approve production receipt retention.
The isolated database fixture now separates its symbolic order, line and payment copies from policy, receipt and audit records. A partial set is not accepted as a complete order. These are fictional single-copy surfaces, not Shopify's actual line/payment cardinalities or an approved whole-payload retention policy.
Fresh-connection fixture tests also distinguish an uncommitted session close from a confirmed commit with its caller result withheld. Recovery still checks current policy before replay; finding a receipt does not authorise processing. These controlled cases are not proof of network-fault or server-crash recovery.
The same fixture also checks recovery after a database lock timeout. A retry must use a fresh transaction and current evidence; it must not assume the other writer failed or restore tracking suppressed while it was waiting. These checks do not enable automatic retries or live privacy processing.
A separate synthetic diagnostic identifies selected fields read by paid-order ingestion, without returning their values. This is not a retention allowlist: unknown fields and buyer/address branches remain unresolved, and raw order, line and payment redaction remains blocked.
A separate, test-only evidence bundle now preserves the approved narrow subset of fictional paid-order inputs as parsed values. It refuses an entire input containing unknown, buyer/address or excluded free-text fields; it does not remove those fields from real orders. Missing values, explicit nulls and both money alternatives remain distinct. Original event receipts and their raw-byte fingerprints remain separate and unchanged. This is not a retained-payload policy, anonymisation or permission to ingest; live processing stays disabled.
A read-only synthetic comparison additionally checks whether candidate evidence matches one supplied original delivery receipt and its original body. Its value-free result describes inputs captured at invocation, not complete stored history or permission to write. It cannot make changed raw bytes equivalent by normalising their parsed values, and it leaves the original receipt unchanged. The comparison authorises neither live ingestion nor privacy processing.
A separate synthetic check compares supplied order and payment fragments with the expected JSON-serialised source. It reports missing or mismatched copies and explicitly flags negative-zero normalisation rather than claiming lossless source preservation. It does not discover database rows, prove their provenance, map line records or repair anything; original receipts remain unchanged.
Synthetic line-mapping tests separately exercise the existing ingestion rules. Rejected lines do not receive a line number; accepted lines are numbered in their remaining order. Reordering or changing which lines qualify can shift those numbers, so they are not proof of stable source-line identity. These tests do not discover stored rows or enable line-copy processing.
A test-only composition also compares supplied synthetic line fragments with the slots produced from receipt-matched source evidence. It refuses malformed, foreign-scope or duplicate-slot fragments and reports missing, mismatched and unexpected slots without repairing them. A match is not proof of a stored row, stable identity, complete coverage or permission to process privacy requests.
Cross-delivery tests hold all supplied copy observations against the same chosen original. They expose mixtures from different same-order deliveries when the copied values differ, including shifted line slots. Conversely, identical copied values can match more than one original: a match cannot identify a delivery or decide which order state is current. Original receipts remain separate and unchanged; live processing remains disabled.
Matching source copies also does not validate calculated amounts. Synthetic checks distinguish the existing price/discount/tax fallback rules from the preserved inputs and exercise numeric overflow. These are observations of current behaviour, not approved financial rules or permission to ingest an order. Financial validity remains a separate, unresolved gate.
A separate synthetic numeric model now checks complete fictional order, line and payment candidates. It requires explicit matching currencies, exact decimal strings and positive whole-unit quantities. Invalid fields or arithmetic overflow refuse the entire candidate, with no partial result. Passing means numeric representability only—not correct accounts, verified source mapping or permission to write. The live ingestion rules and original receipts are unchanged.
Synthetic compatibility tests also show why that numeric check cannot simply be placed before the current calculator: accepted exact amounts can change through its floating-point arithmetic, either crossing the allowed ceiling or remaining in range with different values. Matching raw copies do not prove that derived money is preserved. No runtime correction is enabled by these tests.
The next synthetic source model separates current order observations from creation-time values and original receipts. It uses an explicit versioned GraphQL selection with currency-bearing amounts, preserves zero current line quantities and refuses incomplete or malformed selections without fallback. It does not prove freshest state, payment reconciliation or webhook equivalence, and it does not change the running Sales integration. Live processing remains disabled; original receipts remain unchanged.
A separate synthetic snapshot classifier now checks supplied identity, reconnect generation and changes since a comparison was prepared. It distinguishes older, equal and later modification times; equal-time differences remain a conflict, including source values hidden by display normalisation. A later time is not proof of the latest Shopify state or permission to replace Sales data. No runtime enforcement or original-receipt change is introduced.
The admission policy now has a synthetic request-bound model: one exact query profile and one complete observed response rather than merged reads or pages. It checks declared attempt/version/identity consistency and rejects unsupported metadata or partial results. A qualifying observation can proceed only to mapping review, not Sales replacement. Acquisition is still fictional; latest-state and remote-atomicity claims remain unproved. Live processing is still disabled.
A further fictional review summary now separates current source-labelled order amounts from creation-time totals and historical unit prices. It preserves the tax-inclusion flag and zero current quantities without guessing net amounts, payments or stock actions. Older, unchanged or conflicting observations do not produce a replacement summary. This is not a new Sales screen or an accounting update: the running integration, payment records and original receipts remain unchanged, and live processing remains disabled.
Synthetic accounting checks now demonstrate why an already refund-adjusted current value must not also have the same refunds deducted from it. They keep current observations separate from the financial basis, payment/refund records and original receipts, and expose overlapping discount and tax-inclusive shipping risks. This is test evidence, not a runtime correction or permission to replace Sales totals; unknown tax breakdown remains unresolved and live processing stays disabled.
Further synthetic checks distinguish the Paid status label and Shopify event copy from Sales capture-ledger evidence. A missing local capture is not proof that the buyer has not paid. The tests neither import payments nor authorise a second charge; current order values, currencies and original receipts stay separate, with no runtime or live-processing change.
A separate synthetic transaction review now preserves payment-source fields without treating them as Sales captures. Both currency-labelled amounts and all accepted transaction kinds/statuses remain observations, not reconciled totals. Duplicate IDs, wrong-order references and uncertain declared counts refuse the whole input. Even a matching exact count does not establish complete or current history. No payment status, outstanding calculation, original receipt or runtime behaviour changes; live processing remains disabled.
The next synthetic model reviews capture eligibility without inserting payment records. It separates successful sale/capture observations from authorisations, refunds, unsuccessful attempts, manual payments and provider-test payments. Repeated evidence retains the same identity across reconnects; conflicting amounts or identities block the entire review. Pending observations cannot erase previously observed success. Parent evidence, explicit currency and positive amount checks remain mandatory for an eligible capture. This is not proof of settlement, a complete payment history or an existing ledger entry. Sales totals, original receipts and live processing remain unchanged.
A separate fictional review now adds processing-time and payment-family observations without creating ledger records. Missing time or method stays unresolved; the model never substitutes arrival time, the current clock or an "Other" method. Only explicit card-type evidence supports a proposed card classification. Pending observations remain separate from successful payment evidence, and additional metadata cannot create another capture. Original receipts, Sales accounting and live processing remain unchanged.
An isolated database integration now connects those fictional capture checks to real payment-ledger tables and the existing Sales read queries. It is confined to a disposable database, not a running shop. It does not update order totals, payment status, stock or original receipts, and it does not enable privacy exports or deletion. Successful isolated tests are not a live-release approval.
A further source migration now enforces transaction uniqueness in PostgreSQL, including when a writer bypasses the adapter's advisory lock. Declared Shopify captures require a canonical customer/environment/shop/transaction identity, with no new identity on reconnect or reassignment to another Sales order. This has not been deployed to an application database. Sales now has an exact, read-only capture-evidence projection, verified against the real ledger in an isolated synthetic database. Its visible ledger remains POS-only. The subsequent outstanding/POS correction uses exact signed balances and an atomic capture, status, audit and submission-nonce transaction, also verified only against the isolated real ledger. It does not import Shopify captures, change the Shopify accounting basis or enable processing. Provider acquisition, complete history, reconciliation, other accounting consumers and privacy execution remain separate activation gates. See Sales payment rules.
The separate POS browser-form boundary now has session-first authentication and a user/account-bound CSRF token. Its isolated authenticated request-path checks do not enable Shopify ingestion or privacy processing; the atomic submission nonce and original receipts remain separate and unchanged. See POS payment forms.
A single-order payment acquisition path is now tested with fictional responses through that isolated ledger integration. It checks the store, order and saved connection before accepting payment evidence, refuses stale responses after a connection change, and preserves existing receipts on replay. This is not a live Shopify request or an enabled importer. Matching transaction counts do not prove a complete or up-to-date payment history; those release checks remain open, along with reconciliation and the other accounting consumers.
That isolated path now exercises the actual encrypted-token resolver too: fresh credentials are reused, expired credentials are rotated atomically, and competing refresh/reconnect attempts cannot silently replace a request's saved connection. A stale attempt is refused and must begin a fresh acquisition. Synthetic verification reaches the real ledger and Sales reader; it does not change the POS-only display or enable Shopify imports. Original receipts, order state and stock remain unchanged. A successful isolated token refresh may remain saved even if later payment acquisition is refused.
The isolated acceptance now begins with the actual signed-session Connect Shopify and OAuth callback, rather than seeded access credentials. It verifies one-use code exchange, encrypted storage, authenticated shop identity and the same acquisition-to-ledger/Sales path. Invalid or overlapping callbacks cannot exchange twice. Profile failure preserves the committed token pair but cannot establish a new shop identity; a stale profile cannot overwrite a changed grant. This remains synthetic-only: no running-store import, provider call or privacy processing is enabled, and original receipts remain unchanged.
If connecting is interrupted
Starting or abandoning a reconnect does not replace the saved store connection. A rejected return from Shopify does not overwrite its saved authorisation or mark the existing connection as failed. Complete approval within 15 minutes, using the same signed-in ChannelWeave user and store; otherwise start Connect to Shopify again. This preserves the saved connection details, but does not guarantee that Shopify has left an earlier authorisation valid.
What you will see when connected
- Store name refreshed from Shopify when the status check succeeds
- Connected-since timestamp
- Connected status badge
- Shopify pulse metrics on the same card
- Shop average rating when Shopify review data is available
Choose inventory location when publishing
In a Shopify draft, choose Publish to Shopify, then select the intended Shopify inventory location in the confirmation. No location is selected automatically, even if only one is available. This is a Shopify location, not a ChannelWeave Stock storage location.
The confirmation loads current active locations from your connected shop. If loading fails, use Retry. Reopening the confirmation clears your previous choice and loads a fresh list. ChannelWeave checks the selected shop and location again before creating the product; if either has changed, reopen the confirmation.
The choice applies to every variant in that publication and is retained with the published listing for subsequent stock updates. It is not a store-wide default or a saved setting on an unpublished draft. Publishing does not change Shopify's location settings or order-routing rules.
Shopify order delivery status
ChannelWeave records Shopify fulfilments from these webhooks:
fulfillments/createfulfillments/update
Each Shopify fulfilment and its line quantities are stored separately. This means one shipment does not make the whole order look dispatched when other lines are still waiting:
- A pending fulfilment does not count as dispatched.
status=successcounts only that fulfilment's mapped line quantities as dispatched.- The order is Partially fulfilled while only some ordered quantities have shipped.
- The order becomes Dispatched only when every non-cancelled line is fully fulfilled.
shipment_status=deliveredcounts delivery evidence for that fulfilment. The order becomes Delivered only when every non-cancelled line has delivery evidence.
Duplicate webhook deliveries do not create duplicate fulfilments. Older out-of-order updates cannot replace a newer state for the same fulfilment. Tracking details remain attached to the individual fulfilment, while the order header shows the latest available shipping summary.
The Shopify app connection must include the read_fulfillments scope, and the
Shopify app/webhook setup must subscribe the store to the fulfilment webhook
topics. If a store was connected before this scope was requested, reconnect
Shopify before relying on automatic delivery updates.
Shopify order cancellations
ChannelWeave accepts signed orders/cancelled updates from Shopify. The
matching Shopify Sales Order is marked Cancelled, its undisbursed Warehouse
reservations are released, and the restored availability is propagated to linked
listings.
When the cancellation includes a confirmed successful refund transaction, ChannelWeave records that refund and updates the payment status to Partially refunded or Refunded according to the confirmed amount. Repeated updates do not duplicate the refund or release stock twice. The original payment history is retained; a cancellation by itself is not evidence of a refund.
Pending or failed refunds are not counted as returned money. A refund that completes later, outside the cancellation update, is not automatically covered by this flow. Refund tax attribution remains unknown unless separately established. Check the refund against Shopify before treating reconciliation as complete.
The Shopify app/webhook configuration must subscribe each connected store to
orders/cancelled; without that subscription, cancellations made in Shopify
cannot release ChannelWeave reservations automatically.
Shop average rating
The Shopify card can show a Shop average rating on Control Centre.
- If Shopify review data is available for your store, the card shows the current average rating.
- If review data is not available yet, the card keeps the Shop average rating unavailable state.
Shopify messages in Inbox
ChannelWeave can show Shopify buyer messages in Inbox → Messages.
Before you start
- ChannelWeave has already enabled the app-proxy feature on our side.
- You still need one setup step per store/theme so your Contact page sends messages into ChannelWeave.
- If you do not edit theme code yourself, send these steps to your theme developer.
Step 2: Configure your store Contact form (per store)
Use the ChannelWeave Shopify section template when the theme's standard contact form does not let you change the submit target.
Download the ChannelWeave Shopify contact-form template before editing the theme.
Setup steps:
- Open admin.shopify.com and choose your store.
- Go to Online Store → Themes.
- Click … → Edit code on the theme you want to update.
- Under Sections, click Add a new section.
- Name the section
channelweave-contact-form. - Replace the generated section code with the contents of the downloaded ChannelWeave template.
- Click Save.
- Go back to Online Store → Themes → Customize.
- In the theme editor, click the page selector at the top (often Home page) and switch to Pages → Contact.
- Remove or hide the standard Shopify Contact form section.
- Click Add section and choose ChannelWeave contact form.
- Keep ChannelWeave app proxy path as
/apps/channelweave-contactunless your store uses a custom proxy path. - Save the theme.
- Send a test message on your storefront Contact page.
- In ChannelWeave, open Inbox → Messages and confirm the message appears.
Implementation notes:
- Shopify’s standard
{% form 'contact' %}posts to Shopify’s own contact endpoint by default. On its own, it will not send messages to ChannelWeave Inbox. - The ChannelWeave section posts to the Shopify app-proxy path and submits the
message as
contact[body], which ChannelWeave ingests as a Shopify Buyer question. - The section includes an in-page success state for storefront customers and a no-JavaScript fallback that still posts to the same app-proxy path. After a successful submission without JavaScript, customers see a Message sent confirmation in the store theme and a Return to the store link. There is no need to submit the message again.
If you get stuck
Send this brief to your theme developer:
Update our Shopify Contact form to post to
/apps/channelweave-contact(or our custom app-proxy path), and include message fieldcontact[body].
Note: Shopify’s standard{% form 'contact' %}alone is not enough for ChannelWeave Inbox ingest.
Listings
Shopify draft creation is a persisted stock-first workflow.
Use Listings → Drafts → Create draft → Shopify to:
- Choose a stock item first
- Review recent Shopify drafts before creating another one
- Create a stock-linked Shopify draft
- Refine title, description, SKU, price, currency, quantity, category, and SEO fields while preserving a valid linked stock item
- Ask AI assistant to draft a Shopify description from the current stock/listing facts
- Sync images from the linked stock image manifest, upload Media library images, and reorder/remove draft images
- Add optional variant rows by SKU with a Shopify option name/value
- Run Validate to check stock link, content, pricing, quantity, and images
- See an inline quantity warning when the draft quantity is higher than linked available stock
- Publish an active Shopify product from the draft when Shopify is connected with product and inventory write scopes
- Save, archive/unarchive, or delete an unpublished Shopify draft
You can also start from Stock catalogue or a stock item detail page with Create drafts. Tick Shopify in the channel list to create a stock-linked Shopify draft from stock defaults; if eBay or Website are selected at the same time, ChannelWeave creates those drafts in the same run.
Publish to Shopify creates an active Shopify product via Shopify Admin
GraphQL, sends the draft image manifest, applies variant option rows when
present, sets tracked SKU inventory at the store's first active Shopify
inventory location, records the resulting published listing, and stores the
Shopify product id on the draft. Older Shopify connections must be reconnected
if they were authorised before ChannelWeave requested write_products,
write_inventory, and read_locations.
Stock availability updates
For linked published listings with inventory writes enabled, ChannelWeave sends the available quantity from its Stock record to the saved Shopify inventory location, capped by the listing's quantity limit. Keep stock corrections in ChannelWeave rather than independently changing the same quantity in both systems.
Stock-update retries reuse an operation identity so a lost response does not automatically become a second Shopify adjustment. A new availability change is a new operation, even when it returns to a previously used quantity. This is retry protection, not a guarantee that concurrent updates from different systems will arrive in order.
Isolated paid-webhook acceptance
A separate synthetic HTTP integration now connects signed paid-delivery intake for an existing Sales order to transaction acquisition, the real capture ledger and the Sales reader. It stores original delivery bytes separately from retry state. Duplicate deliveries preserve first evidence; conflicting retries are recorded without replacing it. Missing orders remain blocked for review.
Capture inserts and their processing outcome commit together. An expired or replaced worker cannot finish another attempt, and reconnect does not create a second financial transaction. Original receipts, Sales totals/status, lines, refunds, stock and queues are not updated. The current POS-only payment display is unchanged.
The intake is now an application-owned candidate with explicit configuration and canonical intake/work/conflict tables. It is not registered in the running application. Single-item claim/lease/retry orchestration, acquisition and atomic capture/completion now have one application-owned implementation, invoked only by the guarded synthetic harness. A single explicit composition shares the configured app, environment and API version across intake and processing; another app's work cannot be claimed or reported as completed. Intake does not automatically start processing. The current narrow financial policy remains Sandbox-only and synthetic-labelled; moving the implementation does not enable provider requests or authorise live captures. Callback failures with an unknown outcome leave the lease and any committed capture untouched. The rehearsal checks intake before real session, billing, navigation and shared body parsing, without requiring a browser login or cookie.
Verification uses fictional signatures/responses, not Shopify delivery or an enabled importer. The paid-webhook path now uses the restored full local schema with synthetic order, connection and inventory records, plus reviewed missing prerequisites installed only in the isolated test database. The earlier OAuth and authenticated POS regression suites still use minimal fixtures. An additional authenticated Sales GET test uses the restored full schema, real signed sessions, permissions, billing and rendering. It preserves the exact ledger view-model values without exposing the existing POS-only payment UI on Shopify orders. This is not password-login or full running-server acceptance. No real records were copied. New-order ingestion, reconciliation, runtime replacement, deployed schema/grants and explicit deployment/activation approval remain separate requirements.
An additional isolated migration/grant matrix verifies populated preservation, atomic refusal and effective synthetic permissions. It also demonstrates that third-role inherited/default grants can defeat evidence restrictions. The schema-only snapshot contains no deployed permission evidence, so this is not activation approval.