معماری چندکسبوکاری، تجاری و تجارت الکترونیک · فصل ۷ — Connector، Order، Packing، Barcode، Carrier و Notification · 268
Shopify Integration
Design a bounded Shopify connector that pins API/version assumptions, maps external IDs into a canonical internal contract, verifies event/request provenance, records idempotency and replay evidence, and preserves UNKNOWN/RETRY/HOLD states instead of treating vendor delivery as proof of internal completion. میتواند «Shopify Integration» را برای یک workflow محدود با state/contract/approval و verification مناسب طراحی کند.
Lesson summary
Design a bounded Shopify connector that pins API/version assumptions, maps external IDs into a canonical internal contract, verifies event/request provenance, records idempotency and replay evidence, and preserves UNKNOWN/RETRY/HOLD states instead of treating vendor delivery as proof of internal completion.
میتواند «Shopify Integration» را برای یک workflow محدود با state/contract/approval و verification مناسب طراحی کند.
This lesson produces
میتواند «Shopify Integration» را برای یک workflow محدود با state/contract/approval و verification مناسب طراحی کند.
Who it is for
Foundation
Use a field map, one worked scenario and explicit state vocabulary before independent construction.
Repair
Identify the failed evidence dimension, use the smallest mutated fixture and rerun only the affected gate.
Experienced
Compress definitions; require trade-off defense, counterexample analysis and changed-context transfer.
Prerequisites
When “GraphQL succeeded” still does not mean “the integration is correct”
Imagine a Shopify order webhook is retried after a timeout while an inventory mutation has already been sent but its response is lost. If the connector only records “HTTP succeeded” or “the webhook was valid,” critical questions remain unanswered: Which API version actually handled the request? Were the required scopes present? Which canonical identity does the Shopify GID map to? Is this delivery a duplicate or a distinct event? Is the mutation retry the same intent or a new operation? And after a lost response, what actually happened in Shopify?
The lesson output is Shopify Connector Contract and Event Ledger v1. The goal is not to build a generic Shopify client. The goal is to make every observation and mutation reconstructable before it can influence local state.
COM-027 ownership boundary
COM-027 owns Shopify-specific integration evidence: shop identity, GraphQL Admin surface, API version, access scopes, external GID mapping, webhook verification/deduplication, mutation idempotency, retry state and reconciliation. COM-029 owns provider-agnostic connector architecture and COM-028 owns WooCommerce-specific integration.
Three red lines:
Shopify object != canonical local objectvalid HMAC != business completionsuccessful response != permission to invent a new retry identity
Mental model: Shop → Version/Scopes → External identity → Event authenticity → Dedupe → Mapping → Mutation identity → Retry/Reconcile → Local promotion
Auditable flow:
shop_ref → GraphQL Admin + requested/observed version + scopes → external_order_id/GID → webhook topic + webhook/event ID + HMAC + digest → dedupe/order check → schema_mapping_version → mutation + idempotency key + attempt → response/retry state → reconciliation_cursor → canonical_order_id → evidence_ref
Each gate solves a different problem. HMAC does not repair mapping; idempotency does not repair missing scopes; and GraphQL success alone does not close uncertainty after a timeout.
Keep FACT, INFERENCE and DESIGN separate
FACT: Shopify currently documents the GraphQL Admin API as the admin app/integration surface. On 15 September 2026, 2026-07 is the latest stable version and 2026-10 is a release candidate.
FACT: Shopify versioned APIs release quarterly. If an inaccessible version is requested, Shopify can fall forward to the oldest accessible stable version, while X-Shopify-API-Version reports the version that actually fulfilled the request.
FACT: Shopify webhook deliveries include HMAC, X-Shopify-Webhook-Id, X-Shopify-Event-Id, API version and trigger timestamp metadata. Delivery ordering is not guaranteed and duplicate delivery can occur.
FACT: Shopify's documented idempotency behavior applies only to mutations that support it. Retries of the same intended operation reuse the same key, distinct operations need distinct keys, and the documented key retention window is 24 hours.
INFERENCE: Because API version, event delivery and mutation retry can drift independently, their evidence should be stored separately so transport success cannot silently become business truth.
DESIGN: MAKNOW gates local promotion on mapping/version, event identity, retry identity and reconciliation. That policy is local design, not a Shopify mandate for every app.
The 23-dimension Shopify contract
| case | decision | api_version | access_scope_set | schema_mapping_version | event_id_or_dedupe_key | authenticity_check | idempotency_key | retry_state | evidence_ref |
|---|---|---|---|---|---|---|---|---|---|
| baseline | ACCEPT_CONNECTOR | 2026-07 | read_orders write_inventory | map-v7 | wh-1001 | VERIFIED | idem-1001 | CLOSED | evidence://COM-027/baseline |
| scope_missing | HOLD_ACCESS_SCOPE | 2026-07 | read_orders | map-v7 | wh-1002 | VERIFIED | idem-1002 | BLOCKED | evidence://COM-027/scope |
| duplicate | HOLD_EVENT_DEDUPE | 2026-07 | read_orders write_inventory | map-v7 | wh-dup-1 | VERIFIED | idem-1003 | DUPLICATE_EFFECT_DETECTED | evidence://COM-027/dup |
| version_transfer | HOLD_MAPPING_VERSION | 2026-10 | read_orders write_inventory | map-v7 | wh-1004 | VERIFIED | idem-1004 | PENDING_MAPPING | evidence://COM-027/version-transfer |
Gate 1 — Identify the shop and connector explicitly
connector_id and shop_domain_or_tenant_ref must be unambiguous. “Shopify production” is not sufficient audit identity. Real secrets or access tokens never belong in the learner artifact.
Gate 2 — Pin API surface and version
api_surface is GRAPHQL_ADMIN for this lesson. api_version is part of the contract, not an incidental URL setting. The current baseline requires a production target marked STABLE_SUPPORTED; a release candidate is useful for migration testing, not the production baseline here.
Gate 3 — Compare requested and observed version
If a retired version falls forward, the request can still succeed while the effective schema contract differs from what the caller requested. Record the actual response version. A mismatch produces HOLD_API_VERSION until mapping assumptions are rechecked.
Gate 4 — Separate access scope from authentication
A valid session/token proves authentication, not that the required query or mutation scopes are available. access_scope_set must satisfy the operation. Missing scope yields HOLD_ACCESS_SCOPE.
Gate 5 — Never treat Shopify GID as canonical identity
external_order_id and external_gid are Shopify identities. canonical_order_id is produced through a versioned mapping. Downstream logic should not couple itself to vendor ID shapes.
Gate 6 — Revalidate mapping when schema meaning changes
schema_mapping_version records which field contract the adapter understands. API version changes, deprecation or semantic drift invalidate the previous success assumption until mapping is reviewed and, when needed, bumped.
Gate 7 — Verify webhook HMAC against the raw body
authenticity_check must verify the delivered raw body. Parsing or transforming before verification can destroy the evidence. Invalid or unknown HMAC yields HOLD_AUTHENTICITY.
Gate 8 — Keep delivery identity separate from business meaning
event_id_or_dedupe_key records delivery identity. Receiving the same X-Shopify-Webhook-Id twice must not produce a second side effect. X-Shopify-Event-Id can strengthen correlation across deliveries from one merchant action, but does not replace business mapping.
Gate 9 — Do not assume webhook ordering
Shopify does not guarantee ordering. A late old update must not regress canonical state merely because it arrived later. Preserve timestamp/version evidence and evaluate progression explicitly.
Gate 10 — Use vendor idempotency only where supported
A string called “idempotency key” is not enough. First establish that the mutation supports Shopify's documented idempotency behavior. Otherwise keep a local stable operation identity and dedupe path.
Gate 11 — Same intent retry, same key
A timeout retry of the same operation reuses the same key. Generating a new UUID for every attempt defeats duplicate protection. Conversely, distinct operations require distinct keys even if variables look similar.
Gate 12 — Lost response after send means UNKNOWN, not definite failure
If the request left the process but the response was lost, response_state=UNKNOWN_AFTER_SEND. Blind retry before reconciliation can duplicate work. Idempotency helps, but the outcome still needs bounded reconciliation.
Gate 13 — Cached response is not the business oracle
Shopify documents that a retried idempotent operation can receive a cached response and that reconstructed response data can reflect current database state. Transport safety therefore does not replace domain evidence.
Gate 14 — Missing decisive evidence fails closed
Unknown version, scopes, mapping, HMAC, event identity, idempotency or reconciliation yields a typed HOLD/UNKNOWN state rather than optimistic acceptance.
Worked examples
1. Stable baseline → ACCEPT_CONNECTOR
The shop is explicit, GraphQL Admin is pinned to stable 2026-07, requested and observed versions match, scopes are sufficient, GID mapping is versioned, webhook HMAC is verified, delivery is deduplicated, and mutation/retry/reconciliation evidence is closed.
2. Required scope removed → HOLD_ACCESS_SCOPE
The session is valid but the operation lacks required authorization. Authentication does not prove authorization.
3. Invalid HMAC → HOLD_AUTHENTICITY
A plausible order-shaped payload is still unusable for promotion before authenticity is verified.
4. Same webhook creates a second side effect → HOLD_EVENT_DEDUPE
Duplicate delivery must be idempotent. Double fulfillment or adjustment is connector failure even if both HTTP deliveries succeeded.
5. Timeout retry uses a new key → HOLD_IDEMPOTENCY
The intent is the same but the key changed, so the provider cannot recognize it as the same protected operation.
6. Retired requested version falls forward → HOLD_API_VERSION
HTTP success is insufficient. Requested/observed mismatch means schema assumptions must be revalidated.
7. Transfer: version changes and a mapped field is deprecated → HOLD_MAPPING_VERSION
Even when the new version is stable, the previous mapping is not the oracle. Revalidate or bump it.
8. Response is lost after send → HOLD_RECONCILIATION
Do not issue definite success or failure until query/ledger evidence closes the operation outcome.
Common failure modes
- using an implicit “latest” instead of a pinned version;
- ignoring
X-Shopify-API-Versionand fall-forward; - treating a valid token as sufficient scope;
- leaking Shopify GID shape into the local identity model;
- keeping an old mapping after version/schema change;
- verifying HMAC after the request body has been transformed;
- applying the same
X-Shopify-Webhook-Idtwice; - trusting arrival order;
- attaching idempotency semantics to a mutation that does not support them;
- generating a new key for the same-intent retry;
- reusing one key for distinct operations;
- turning post-send timeout into definite failure;
- equating a provider response with business completion;
- copying a baseline result into transfer without recomputation.
Alternatives and trade-offs
Webhook-only: low latency, but duplicate/out-of-order/missed deliveries and versioned payloads remain operational concerns. It is insufficient for unresolved outcomes by itself.
GraphQL polling-only: gives controlled snapshots but increases latency and API usage. Useful for reconciliation, not necessarily the only ingestion path.
Webhook + bounded GraphQL reconciliation: more contract work, but closes ambiguity better and is the lesson's preferred bounded pattern.
Vendor GID downstream: fast start, high coupling to Shopify semantics.
Versioned Shopify adapter: more design effort, but keeps version/scopes/mapping/retry evidence at the provider boundary.
Experienced bridge: eighteen review questions
- What exactly are
connector_idand shop reference? - Which API surface is used?
- Which API version was requested?
- Which version actually fulfilled the response?
- What is the support/stability status of that version?
- Which access scopes are required?
- How are external order ID and GID traced?
- Which mapping version produced the canonical order ID?
- What triggers a mapping-version bump?
- What are webhook topic and delivery ID?
- How was HMAC verified against the raw body?
- What payload digest is recorded?
- How are duplicate deliveries neutralized?
- How does the ledger prevent regression from out-of-order delivery?
- Does the mutation support Shopify idempotency?
- Did the same-intent retry reuse the same key?
- What are response, retry and reconciliation states?
- Can a reviewer reconstruct the outcome from
evidence_refalone?
Guided and independent practice
The offline lab contains eight fixtures: baseline, insufficient scope, invalid HMAC, duplicate webhook, out-of-order regression, same-intent retry with a new key, API version fall-forward, and a transfer case with stale mapping after a version change.
Independent task: migrate an integration from 2026-07 to a later API version where one adapter field is deprecated. State which evidence can be reused, which evidence must be rechecked, what requires a mapping bump, and why a successful request on the new version alone does not authorize local promotion.
Verification evidence
A passing artifact fills all 23 dimensions or returns an explicit typed non-success state. No real secret/token is stored; requested and observed versions are traceable; scopes are explicit; GID never becomes canonical truth directly; HMAC and event identity are checked before event acceptance; duplicate/out-of-order behavior cannot create extra side effects or regression; idempotency is limited to supported mutations; the same-intent retry preserves operation identity; and post-send uncertainty closes through reconciliation.
The lab is fully synthetic and offline. It requires no network, real shop, token or customer data.
Assessment and transfer
To complete the lesson you must be able to:
- explain COM-027's boundary against COM-028 and COM-029;
- build Shopify Connector Contract and Event Ledger v1;
- separate requested version from observed version;
- verify access scope separately from authentication;
- map external GID through a versioned identity mapping;
- handle webhook authenticity, deduplication and ordering independently;
- apply retry idempotency only to supported operations with stable key reuse;
- rebuild the version-change transfer without blindly reusing the baseline decision.
References and freshness
- Shopify GraphQL Admin API — rechecked 15 September 2026;
2026-07is the latest stable version and the versioned endpoint is current FACT evidence. - Shopify API versioning — rechecked 15 September 2026; quarterly releases, support lifetime, release candidate and fall-forward semantics are FACT evidence.
- Shopify Webhooks / Verify deliveries / Delivery structure — rechecked 15 September 2026; HMAC, delivery/event IDs, version header, duplicate and out-of-order behavior are FACT evidence.
- Shopify Implementing idempotency — rechecked 15 September 2026; supported-mutation scope, same-intent key reuse and the documented 24-hour retention window are FACT evidence.
- Mapping, typed HOLD states and local-promotion policy are MAKNOW DESIGN.
Assessment
There is no answer key in this lesson pack; answer it yourself and mark honestly. "Needs review" sends the lesson to your review queue.
COM-027-S1 retrieval
Explain what COM-027 owns and name one adjacent non-goal.
COM-027-S2 application
Build Shopify Connector Contract and Event Ledger v1 for the baseline fixture.
COM-027-S3 diagnosis
A mutation timed out after send and a retry generated a new idempotency key. Diagnose the unsafe boundary and minimal repair.
COM-027-S4 transfer
The Shopify API version changes and a mapped field is deprecated. Recompute version/mapping evidence without copying the prior success result.
Assessment rubric
| Dimension | Sufficient evidence |
|---|---|
| Shop/API identity | connector, shop, API surface and requested/observed version are explicit |
| Access boundary | required access scopes are explicit; no real token is embedded |
| Identity mapping | Shopify ID/GID maps through a versioned adapter to canonical identity |
| Webhook authenticity | HMAC and payload digest are verified before event acceptance |
| Delivery safety | duplicate and out-of-order deliveries cannot create duplicate effect or regression |
| Mutation idempotency | supported mutations use stable same-intent keys; distinct operations differ |
| Reconciliation | unknown-after-send or retry uncertainty closes against bounded evidence |
| Transfer | version/schema change is recomputed rather than copied from baseline |
References & freshness
Case studies
Lab files
python run_checks.py را اجرا کنید. همه fixtureها ساختگیاند و هیچ shop واقعی، شبکه، access token یا داده مشتری لازم نیست.