Skip to content
TCGPlaytestdocsDashboard
Documentation Build your integration
Build your integration

Checkout widget

Connect finished images to a ready-made review and checkout experience.

Standalone checkout widget

For verified return handling, configure externalRef for your saved checkout revision, returnUrl on your registered website, and an async onCartReady(cart) callback to bind the cart to your customer on your server. The callback is awaited before opening checkout and may run again on retries. See the complete test flow.

The widget uses a public widget_partner_key assigned to your account automatically. Accept partner terms, copy the personalized snippet from your dashboard and connect your editor's finished images. You do not need a secret API key, your own storage or a server cart endpoint for this integration.

Clicking the widget button opens a TCGPlaytest-branded modal with each design's front, matching back, quantity and total card count. The customer reviews the cards, then clicks Proceed to checkout. Only then do uploads and validation begin. Once the artwork is ready, the widget opens checkout inside your page for contact/shipping details and image-rights confirmation. Only the payment step opens Stripe. The modal does not collect card payment credentials.

  1. Under 3. Add TCGPlaytest to your website, add any additional HTTPS website origins. Your registered website is allowed automatically. Exact origins only: production and preview domains must be added separately; no wildcards or localhost.
  2. Copy your snippet. Use data-mode="test" for a simulated checkout, or data-mode="live" for the configured checkout website. Use the snippet's partner-platform and checkout origins together.
  3. Pass finished PNG/JPEG Blob/File objects or async functions that return them. Return { items, sharedBack } to apply one selected back to single-sided designs. Supply an item-specific back for double-sided cards. Omit both to use the TCGPlaytest default back. The widget handles back selection, previews and uploads. Quantities count physical cards.
Standalone widget browser integration
// Run after the personalized widget script has loaded.
// Export the finished card design, including its text and frame.
TCGPlaytest.configure({
  getItems: async () => ({
    sharedBack: sharedBackFile,
    items: [
      { label: "My card", front: () => renderFinishedFront(), quantity: 4 },
      { label: "Another card", front: () => renderAnotherFront(), quantity: 1 }
    ]
  }),
  externalRef: "optional-shop-order-reference"
});
// The default floating button opens the branded front/back review modal.
// For your own button, add data-auto-button="false" to the snippet:
printButton.onclick = () => TCGPlaytest.open().catch(error => {
  console.error(error.message); // The widget also shows the error in its dialog.
});
// After editing the deck, reset a previous attempt before starting a new one:
// TCGPlaytest.reset();
// Keep the default dialog to show previews and Proceed to checkout.
// Advanced custom UIs can explicitly opt out with showDialog:false.

The widget requests an opaque one-hour customer session, uploads directly to TCGPlaytest-owned private storage, creates one cart and polls every four seconds. It opens embedded checkout only after validation opens the cart. The backend copies your accepted terms and saved affiliate preference. Orders and cart analytics belong to your partner account. The customer certifies image rights at checkout.

The widget uploads up to three files at a time and uploads a reused shared-back Blob only once per attempt. Retrying in the same page keeps successfully uploaded files, including completed fronts whose backs still need uploading. No extra partner configuration is required. Session expiry can require a new attempt. Upload quotas still apply; validation runs in the background after upload.

An item's back takes priority over sharedBack, preserving double-sided designs. Single-sided cards use the shared back when supplied, otherwise the TCGPlaytest default back. Partners do not upload the default back or implement the selection rule. The original array return shape remains supported for individual front/back pairs. A shared back can also be an async Blob factory.

The branded widget also offers Print both faces separately on cards with a supplied back. The customer can turn that front/back pair into two physical cards, each using sharedBack or the TCGPlaytest default. Previews, quantities and the printing estimate update together. Print front and back together restores the original pair. This choice requires no partner-side logic or new configuration; a supplied back is never split automatically. Once uploads start, the order layout stays fixed for retries. Use Use current designs to start a different order.

Previews render up to three faces at once. Low-resolution previews show a warning and remain orderable. The backend records source dimensions and the hosted checkout asks customers to acknowledge reduced quality. Missing or corrupt files still need replacing.

The modal shows an estimated printing subtotal as soon as quantities are known. Call TCGPlaytest.estimate(quantity) to display the same estimate in your editor. It returns {currency, quantity, unit_amount, subtotal_amount, excludes}, with amounts in cents. This uses GET /v1/estimate?quantity=10 and needs no API key. Shipping, tax and discounts are excluded; the final price is recalculated at checkout. An unavailable estimate never prevents checkout.

The optional label names a design in the preview. Front/back image factories are cached for the review attempt, so checkout uploads the same files the customer reviewed. Designs are paginated in groups of six, with quantities shown per design. Closing the modal before proceeding creates no uploads or cart. The preview shows supplied artwork; 2 mm print bleed is prepared by the backend during validation.

Retries reuse the submitted cart, including after a page refresh in the same tab. The modal offers Resume checkout and Use current designs for a previous attempt. A resumed cart after refresh explicitly reports that its previews are unavailable; it never substitutes new images into that cart. To change a submitted order programmatically, call TCGPlaytest.reset() when the widget is closed. Failed or expired carts need a new attempt; no API-key rotation is needed. The widget must receive your images through the frontend connection above; a script cannot discover arbitrary editor data automatically.

Field / limitDescription
POST /api/widget/sessionsJSON {partner_key, mode}. Returns 201 {token, expires_at, mode, max_cards, max_image_bytes}. Browser Origin must match an allowed website. No secret API key.
POST /api/widget/uploadsBearer session token; JSON {request_id: UUID, content_type, size}. Returns 201 {id, upload_url, method: PUT, headers, max_bytes, upload_expires_at}. PUT the bytes to that URL without the session token or cookies. Reuse request_id only for an identical grant retry.
POST /api/widget/cartBearer session token; JSON {items:[{front_upload_id, back_upload_id?, quantity}], external_ref?}. Only files from this session are accepted. Returns the cart shape documented above, 202 while validating. One immutable cart per session supplies idempotency automatically.
GET /api/widget/cartBearer session token. Returns only that session's cart; there is no arbitrary cart ID parameter. Polling does not consume upload or session quotas.
Limits60 new sessions per partner/hour; 2,000 upload grants per session; 10,000 grants per partner/rolling 24 hours. Defaults: 60 / 2,000 / 10,000; configured operator overrides are reflected here. Each distinct front/back upload uses a grant reserving up to 20 MiB; copies may reuse an uploaded image. A deck with 100 distinct fronts and 100 distinct backs needs 200 grants. Normal image and cart limits still apply. Widget quotas are separate from API-key rate limits.
Storage lifetimeStorage upload grants last two hours and cannot overwrite an existing object. Unused staging files are removed after the one-hour session plus the grant window. Claimed staging files expire with the cart, subject to that minimum window and legal holds. Validated print files follow the cart retention policy.
Errors401 widget_session_required/expired: start a new session. 403 origin_not_allowed/widget_unavailable/invalid_upload: fix account, website or file ownership. 409 idempotency_conflict: reset for changed inputs. 429 widget_limit: honor Retry-After; repeated quota failures need the operator. Normal validation errors are returned on the cart.

The public code is an identifier, not a secret or proof of the caller's identity. Allowed origins restrict browser use; they do not authenticate non-browser callers. The service enforces expiring sessions, upload quotas, partner status and file ownership independently. Keep the session token private to the customer's tab.

Existing snippets without data-partner-key keep the legacy server-launcher behavior using your own cart endpoint. New snippets use the standalone flow above.

24 topics

Get startedOverview

Everything you need to turn artwork into printed cards.

Get startedFirst order in 10 minutes

A runnable sandbox example from test key to verified webhook delivery.

Get startedOrder with AI · MCP

Connect ChatGPT, Claude, Gemini or your developer tools, preview your cards and check out without a TCGPlaytest account or customer API key.

Get startedTest checkout & events

Simulate checkout, verify the order, return to your store and trigger signed test events.

Get startedQuickstart & authentication

Set up your account, protect your API key and make your first integration.

Get startedHow checkout works

Create a cart, wait for artwork validation and open checkout on your website.

Get startedCode examples

Working server examples in your preferred language.

Build your integrationCheckout widget

Connect finished images to a ready-made review and checkout experience.

Build your integrationEmbedded checkout

Keep delivery details and artwork confirmation inside your own website.

Build your integrationCheckout branding

Make checkout feel like your store with saved branding and style presets.

Build your integrationCustom success page

Return customers to your registered website after verified payment.

Build your integrationStripe Connect payouts

Set your card selling price and receive earnings, with a 5% reserve for 15 days.

Build your integrationPrinting credits

Use your balance for printing, with customer shipping and tax paid through Stripe.

API referenceUpload artwork

Request a temporary upload URL. Send file bytes directly to private storage.

API referenceCreate a cart

Submit finished artwork, quantities and your success URL for validation.

API referenceRetrieve a cart

Read validation progress, quality warnings and checkout availability.

API referenceRetrieve an order

Track a funded manufacturing order from payment to shipment.

API referenceWebhooks

Receive signed order updates on your server.

ResourcesArtwork & API limits

File specifications, card limits and request allowances for this deployment.

ResourcesErrors & retries

Understand failures and retry safely without creating duplicate carts.

ResourcesGoing live

Partner launch checklist: verified orders, allowed origins and live checkout.

ResourcesAffiliate codes

Apply your approved affiliate preference to new carts.

ResourcesExpiry & retention

Understand cart lifetimes, artwork storage and cleanup.

ResourcesTerms & image rights

Know your responsibilities and the customer acceptance requirements.