Skip to main content

Create an online store

This guide is for developers and agencies who connect a website (a custom site or another e-commerce platform) to Wino with the API.

Info

On PrestaShop or WooCommerce? You don't need to code anything: use Wino's official plugins. See Plugins.

How it works​

  1. Map Wino's catalog to your website's: each Wino variant becomes a product of your store.
  2. Import the catalog: read every page of variants.
  3. Stay in sync: webhooks and a regular check tell you what changed.
  4. Send your sales back: each online order becomes a stock movement in Wino.

Wino is the source of truth: the catalog, prices and stock go from Wino to your website, and the sales of your website go back to Wino as stock movements.

Read Catalog and Webhooks first, and develop on staging with a staging key (see Environments).

1. Map the catalog​

Use the GraphQL schema to find the fields you need. This query is a good start; adapt it to your website. The API has no product images: manage them on your website.

REQUEST /graphql

query CatalogForMapping($shopId: String!, $after: String) {
variants(first: 50, after: $after, filterBy: { shopIds: { _in: [$shopId] } }) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
id
name
formattedName
formattedDescription
active
bulk
capacityUnit
capacityPrecision
product {
id
kind
name
tax {
value
}
}
stock {
rawQuantity
}
variantPrices(first: 50) {
edges {
node {
valueIncludingTax
valueExcludingTax
price {
name
taxIncluded
}
}
}
}
}
}
}
}
{ "shopId": "<your shop ID>" }

With a key for several shops, filter by the shop of the website, or you'll mix the catalogs of every shop. Use valueIncludingTax or valueExcludingTax depending on price.taxIncluded: true means the list's prices are set tax included.

Note

Treat each Wino variant as a product of your store, and list the catalog with the variants query. See Catalog. A page has 50 variants at most: fetch the next ones as explained in Pagination. Don't sell a variant whose active is false: the merchant took it off sale. For a bulk variant, convert rawQuantity with capacityPrecision (1500 with precision 3 is 1.5 kg).

Keep in mind:

  • In most cases, a merchant doesn't want to publish their entire catalog on their online store.
  • The prices in the shop are often different from the prices on the website.
  • Some products are sold by the unit and others in bulk, and their stock works differently (see Bulk variants).

On each variant, the merchant gives or doesn't give a selling price in the price list meant for the website. Ask the merchant which price list it is, and find its id with prices(first: 50, filterBy: { shopIds: { _in: [$shopId] } }) { edges { node { id name taxIncluded } } }. Then read each variant's price in that list:

REQUEST /graphql

query OnlinePrices($shopId: String!, $priceId: String!, $after: String) {
variants(first: 50, after: $after, filterBy: { shopIds: { _in: [$shopId] } }) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
id
variantPrices(filterBy: { priceId: { _equals: $priceId } }) {
edges {
node {
valueIncludingTax
valueExcludingTax
price {
name
taxIncluded
}
}
}
}
}
}
}
}
{ "shopId": "<your shop ID>", "priceId": "<id of the website's price list>" }

RESPONSE /graphql

{
"data": {
"variants": {
"pageInfo": { "hasNextPage": false, "endCursor": "<cursor>" },
"edges": [
{
"node": {
"id": "96835dcb-6dbc-4a44-ac94-3cfae1293eec",
"variantPrices": {
"edges": [
{
"node": {
"valueIncludingTax": 11.5,
"valueExcludingTax": 9.58,
"price": { "name": "Tarif Ecommerce", "taxIncluded": true }
}
}
]
}
}
}
]
}
}
}

A variant with no price in that list isn't sold online: don't publish it on your website.

For quantity-based prices, bulk products and stock, see Catalog.

2. Import the catalog​

Subscribe to webhooks first (step 3), then import: changes made during the import then reach you too.

Go through every page of variants and insert them on your website.

3. Stay in sync​

Subscribe to webhooks to be notified when data changes (see Webhooks):

  • stock: StockActivity notifications;
  • products and variants: Product and Variant notifications;
  • prices: VariantPrice notifications. Prices set through the API with createOrUpdateVariantPrice send none, but they change the variant's updatedAt.

Some changes send no notification at all, for example archiving a variant (see Not every change sends a notification). So also fetch regularly the variants changed since your last run, with variants(first: 50, filterBy: { updatedAt: { _after: $since }, archived: INCLUDED }): it returns archived variants too, with archivedAt, so you can remove them from your website.

4. Send your sales back​

For each order on your website, create a stock movement in Wino. Use the order events (hooks) of your CMS: when an order is placed, send the mutation below.

Before the sale, the product has a certain stock and its own movement history:

REQUEST /graphql

query VariantStockBefore {
variant(id: "96835dcb-6dbc-4a44-ac94-3cfae1293eec") {
id
name
stock {
rawQuantity
}
product {
name
}
stockActivities(first: 10) {
edges {
node {
kind
quantity
comment
}
}
}
}
}

RESPONSE /graphql

{
"data": {
"variant": {
"id": "96835dcb-6dbc-4a44-ac94-3cfae1293eec",
"name": "2017, 0.75L",
"stock": {
"rawQuantity": 6
},
"product": {
"name": "Crozes Hermitage"
},
"stockActivities": {
"edges": [
{
"node": {
"kind": "SALE",
"quantity": 6,
"comment": "N°000045"
}
},
{
"node": {
"kind": "RESET",
"quantity": 12,
"comment": "Inventory"
}
}
]
}
}
}
}

Here, N°000045 is the number of a sales receipt from the shop's cash register.

The stock of this product is currently at 6. For a sale of 4 units on your online store, create a stock movement with createStockActivity: kind: SALE with quantity: 4. The quantity is always positive: the SALE kind is what removes it from the stock. A stock movement can't be removed, so test on staging first (see Environments).

REQUEST /graphql

mutation SellOnline($variantId: String!, $deviceId: String!, $shopId: String!) {
createStockActivity(
input: {
kind: SALE
quantity: 4
comment: "Online N°00001"
variantId: $variantId
deviceId: $deviceId
shopId: $shopId
}
) {
kind
quantity
comment
variant {
id
stock {
rawQuantity
}
}
}
}

Variables (the device ID comes with your key, see Your shop and your device):

{ "variantId": "96835dcb-6dbc-4a44-ac94-3cfae1293eec", "shopId": "<shop id>", "deviceId": "<your device id>" }

For a bulk variant, also send its capacityUnit and capacityPrecision, and the quantity as an integer scaled by 10^capacityPrecision: 1.5 kg of a variant with capacityPrecision: 3 is quantity: 1500, capacityUnit: "kg", capacityPrecision: 3.

Send each order once

A stock movement can't be removed, and the API can't tell that you already sent an order. Your CMS may fire the same order event twice: keep the order numbers you've sent, check before sending, and put the order number in comment to find the movement later.

If you sent a movement but got no response (a timeout, a network error), don't resend it blindly: first look for your order number in the variant's movements created since a little before you sent it.

query FindMyMovement($variantId: ID!, $since: Datetime!, $after: String) {
variant(id: $variantId) {
stockActivities(first: 50, after: $after, filterBy: { date: { _after: $since } }) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
kind
quantity
comment
}
}
}
}
}

Read every page (after = endCursor, until hasNextPage is false) before deciding to resend: a movement you don't find on the first page may be on the next one.

If an order is cancelled after you sent it, or the customer returns products, add the stock back with a REFUND movement of the same quantity.

The API doesn't stop a sale when the stock is short: the stock can go below zero. Check the stock on your website before confirming an order.

Note

At the merchant's request, you can also choose LOSS for the field kind instead of SALE. It appears differently in the shop's cash register.

RESPONSE /graphql

{
"data": {
"createStockActivity": {
"kind": "SALE",
"quantity": 4,
"comment": "Online N°00001",
"variant": {
"id": "96835dcb-6dbc-4a44-ac94-3cfae1293eec",
"stock": {
"rawQuantity": 2
}
}
}
}
}

The response includes the variant with its new stock: 2. The shop's cash register gets the new stock shortly after. You can also query the variant again to see the new stock movement in its history:

REQUEST /graphql

query VariantStockAfter {
variant(id: "96835dcb-6dbc-4a44-ac94-3cfae1293eec") {
id
name
stock {
rawQuantity
}
product {
name
}
stockActivities(first: 10) {
edges {
node {
kind
quantity
comment
}
}
}
}
}

RESPONSE /graphql

{
"data": {
"variant": {
"id": "96835dcb-6dbc-4a44-ac94-3cfae1293eec",
"name": "2017, 0.75L",
"stock": {
"rawQuantity": 2
},
"product": {
"name": "Crozes Hermitage"
},
"stockActivities": {
"edges": [
{
"node": {
"kind": "SALE",
"quantity": 4,
"comment": "Online N°00001"
}
},
{
"node": {
"kind": "SALE",
"quantity": 6,
"comment": "N°000045"
}
},
{
"node": {
"kind": "RESET",
"quantity": 12,
"comment": "Inventory"
}
}
]
}
}
}
}

The new movement appears in the variant's history.

Note

The new stock movement also sends a StockActivity webhook notification to the shop's subscribers, your own integration included. See Receive notifications so you don't write back what you already have.

Next steps​

At this stage of the guide, you have everything you need. When your integration works on staging, get your production key and switch to production: see Environments.

Something doesn't work?​

Check the Errors page and the FAQ. Most problems come from the request itself (key, environment, a field name) or from the CMS: turn on its debug mode and read its logs.

Wino doesn't work on the merchant's website: your integration is yours to maintain.