Custom Etsy REST Integration
OAuth 2.0 PKCE, receipt-driven order ingestion, background sync, and ShipStation label generation — all custom-built on .NET 10 with no third-party SDK dependencies.
Overview
The Etsy integration is a fully custom REST client library built in C# on .NET 10. Rather than relying on third-party Etsy SDKs, this library communicates directly with the Etsy Open API v3, giving complete control over authentication, error handling, and domain-specific business logic for print-on-demand fulfillment.
A companion ShipStation (ShipEngine v2) integration enables shipping-label generation and rate calculation — now required for Etsy fulfillment services that need access to buyer shipping addresses.
Core Architecture
REST Transport Layer
A custom EtsyClient serves as the central HTTP transport for all Etsy API communication:
- Automatic token refresh — before every request, the client checks whether the access token is expiring within a configurable skew window and transparently refreshes it
- Auth-health tracking — every HTTP response is inspected; 401/403 responses increment a consecutive-failure counter and eventually flag the channel for reauthorization
- Reauthorization notifications — when a channel is marked
NeedsReauth, anIChannelAuthNotifierqueues a merchant notification - Typed HTTP verbs —
GetHttpAsync<T>,PostHttpAsync<T>,PostImageAsync<T>,PutHttpAsync<T>, andPatchHttpAsync<T>all returnResult<T> IHttpClientFactoryintegration — proper HTTP client lifecycle management via .NET's built-in factory pattern
OAuth 2.0 & Security
Full OAuth 2.0 with PKCE
Etsy requires the Authorization Code flow with Proof Key for Code Exchange (PKCE):
- Authorization URL generation — constructs the full Etsy OAuth URL with required scopes, a cryptographically random CSRF state token, a code verifier/challenge pair (SHA-256, S256 method), and environment-aware callback URLs
- Reauthorization encoding — when reauthorizing an existing channel, the connection ID is embedded in the state parameter (
state|connectionId) for seamless post-auth routing - Token exchange — exchanges the authorization code for access/refresh tokens via Etsy's public token endpoint
- Refresh token lifecycle — refresh token expiration is tracked; expired refresh tokens trigger a reauthorization flow rather than silent failure
Required Scopes
| Scope | Purpose |
|---|---|
listings_r/w/d | Read, create, update, and delete listing data |
transactions_r/w | Read purchase data (receipts) and post tracking numbers |
shops_r | Read shop information |
address_r | Read shipping addresses |
Auth Health Tracking
- Consecutive 401/403 tracking — the
EtsyClientmonitors every API response and delegates toIMerchantService.HandleUnauthorizedAsyncon auth failures - Automatic recovery — successful API calls clear
NeedsReauth, resetConsecutive401s, and updateLastSuccessAt - Invalid grant detection — if Etsy rejects a refresh with
invalid_grant, the channel is marked for reauthorization with a descriptive reason
Product Management
Product Creation & Synchronization
The EtsyProductManager orchestrates the full product lifecycle between the local catalog and Etsy:
- Listing creation via Etsy's Listings API with title, description, tags, price, quantity, taxonomy, shipping profile, and processing profile
- Full variant mapping — Color (property 200/513) and Size (property 100/514) attribute matrix with per-variant SKU, price, and image assignment
- Shipping profile selection — honors merchant-configured default; falls back to the first non-calculated (fixed-rate) profile; rejects calculated profiles that require unsupported package dimensions
- Processing profile management —
GetOrCreateDefaultProcessingProfileAsyncensures a readiness-state definition exists (default:ready_to_ship, 3–5 business days) - Image upload pipeline — resolves variant mock images, converts to PNG via ImageMagick for Etsy compatibility, uploads via multipart/form-data, and assigns variation images to color property values for per-variant display
- Canonical pricing — prices are set per-size using the most common price among variants sharing a size, satisfying Etsy's
price_on_propertyconstraint
Product Updates
- Incremental variant sync —
UpdateListingInventoryAsyncpushes all non-deleted variants, preserving color/size property values - Automatic variant linking —
LinkMissingVariantsAsyncdetects newly created Etsy inventory products withoutLinkedVariantrecords and links them by SKU match - Idempotent image uploads — checks existing listing images to avoid duplicates
- Deleted variant filtering — variants marked
IsDeletedare excluded from inventory updates
Order Processing
Receipt-Driven Order Ingestion
Orders flow into PrintAura through Etsy receipts:
- Receipt retrieval —
EtsyReceiptsApi.GetReceiptsAsyncfetches shop receipts with optional pagination - Duplicate detection — existing order external IDs are compared against receipt IDs to skip already-ingested orders
- Linked product filtering — only receipts containing transactions for linked products are processed
- Payment verification — only paid receipts (
is_paid == true) are eligible for order creation
Receipt-to-Order Conversion
- Address normalization — country codes are resolved through
EtsyCountry.ToIso2(using Etsy'scountry_id) with fallback tocountry_iso - Missing address detection — receipts with empty shipping information are flagged; orders are placed on hold automatically
- Variant-to-line-item resolution — each transaction's
product_idis matched to aLinkedVariantrecord - Monetary conversion — Etsy prices use amount/divisor (cents); conversion to decimal is applied during order creation
- Shipping & tax — pluggable
IShippingCalculator, tax exemption, and percentage-based shipping discounts
Tracking Number Posting
- Receipt-level tracking —
PostTrackingNumberAsyncposts a tracking code and carrier name to the receipt's tracking endpoint - Carrier default — defaults to USPS when no carrier is specified
ShipStation Integration (ShipEngine v2)
Etsy has restricted shipping-address access to fulfillment services that integrate via ShipStation. PrintAura's ShipStation integration enables buyer shipping address delivery, label generation, and carrier rate comparison.
Label Operations
- Create label — creates a shipping label with full shipment details
- Create from rate — creates a label from a previously quoted rate ID
- Get label — retrieves label details including download URL
Rate Calculation
- Single-package rates — queries carriers with automatic retry and exponential backoff for rate-limited requests
- Multi-package support — UPS sends a single multi-package request (natively supported); USPS splits into individual package requests, fetches rates for each, and combines only services available for all packages
- Resilience — HTTP 429 and carrier-level "Too Many Requests" errors trigger automatic retries with configurable attempt limits; 60-second timeouts for label/rate operations
Background Order Synchronization
A BackgroundService implementation (EtsyReceiptService) polls for new Etsy orders on a 30-minute cycle:
- Initial stabilization delay — waits 90 seconds after application start before the first cycle
- Overlap prevention — a
SemaphoreSlimensures only one processing cycle runs at a time - Concurrent channel processing — up to 5 channels are processed concurrently to respect Etsy rate limits
- Proactive reauth detection — channels with unusable credentials are marked
NeedsReauthbefore sync attempts - Structured run summaries — each cycle logs total channels, processed count, successes, failures, skipped, and elapsed time
Admin Dashboard Components (Blazor)
Connection Management
- Connect Etsy — OAuth PKCE flow initiation with automatic redirect; handles callback code exchange, state verification, double-processing prevention, and reauthorization routing
- Etsy Connection — full channel management: shop info display, permission listing, reauthorization button, order sync trigger, debug instrumentation, and channel deletion with confirmation modal
Product Management
- Edit Etsy Product — comprehensive editor supporting Push to Etsy, Update Etsy Listing, Search & Link, Unlink, per-variant linking, variant match suggestions, and unsaved change guard
Technical Highlights
| Target Framework | .NET 10 / C# 14 |
| Etsy API Version | Open API v3 |
| ShipStation API Version | ShipEngine v2 |
| HTTP Transport | Custom EtsyClient with IHttpClientFactory; ShipStationService with dedicated HttpClient |
| Authentication | OAuth 2.0 with PKCE (Etsy); API key (ShipStation) |
| JSON Processing | System.Text.Json with shared options tuned for each API's conventions |
| Database | Entity Framework Core with IDbContextFactory for scoped contexts |
| Concurrency | SemaphoreSlim for background sync overlap prevention; concurrent channel processing with configurable limits |
| Resilience | Token refresh with expiry skew, auth-health tracking, exponential backoff for rate limits, graceful degradation on API failures |
| Security | PKCE OAuth flow, HMAC signature validation, CSRF state verification, credential isolation per channel |
| Image Processing | ImageMagick for PNG conversion before Etsy upload |
| UI Framework | Blazor Server with interactive components and real-time state management |
This library processes real merchant orders daily alongside its Shopify and WooCommerce counterparts — PKCE OAuth, ShipStation label generation, and background sync included.