Przejdź do treści

Basket lock (zamrażanie koszyka)

Proces składania zamówienia w niektórych sklepach może wymagać zablokowania (zamrożenia) koszyka, aby zapewnić dostępność produktów i niezmienność cen pomiędzy złożeniem zamówienia przez klienta a dokonaniem przez niego płatności. OA umożliwia konfigurację żądania zablokowania koszyka (basket lock), przed rozpoczęciem autoryzacji płatności:

sequenceDiagram
  autonumber
  actor User
  participant OpenApp
  participant Merchant
  Merchant->>User: pokaż widget OpenApp
  User->>User: skan widgeta
  User->>+OpenApp: załaduj koszyk
  OpenApp->>+Merchant: pobierz koszyk
  Merchant->>-OpenApp: wyślij koszyk
  OpenApp->>-User: wyślij koszyk
  User->>+OpenApp: dodaj kupon rabatowy
  OpenApp->>+Merchant: przelicz koszyk
  Merchant->>-OpenApp: wyślij koszyk
  OpenApp->>-User: wyślij koszyk
  User->>+OpenApp: potwierdź
  OpenApp->>+Merchant: zablokuj koszyk
  Merchant->>-OpenApp: wyślij zablokowany koszyk
  OpenApp->>OpenApp: autoryzuj płatność
  OpenApp->>+Merchant: złoż zamówienie
  Merchant->>-OpenApp: potwierdź
  OpenApp->>-User: potwierdź

Request

W tym przypadku OpenApp wykona HTTP POST do endpointu skonfigurowanego w panelu merchanta dla requestu recalculate, ze zaktualizowanym koszykiem w body requestu:

POST <recalculate-url>?lock=true

Jeśli skonfigurowałeś wartość https://shop.example.com/api/openapp/basket, request wykonany przez OpenApp będzie następujący:

POST https://shop.example.com/api/openapp/basket?lock=true

Treść requestu zawiera koszyk użytkownika. Na przykład:

Basket lock request
1
2
3
4
5
6
7
{
  "requestId": "request-id",
  "id": "basket-id",
  "price": { "currency": "PLN", "discounts": [{ "code": "new-discount-code-text" }] },
  "products": [{ "ean": "12312", "id": "id123", "quantity": 3 }],
  "loggedUser": "user-id-from-webshop"
}
Basket lock schema
{
    "description": "Basket recalculate request",
    "additionalProperties": false,
    "type": "object",
    "properties": {
        "requestId": {
            "description": "The ID of the request as generated by OpenApp.",
            "maxLength": 36,
            "type": "string",
            "title": "requestId"
        },
        "id": {
            "description": "A null value for the basket id indicates that this is a request to create a new basket for a repurchase.",
            "maxLength": 36,
            "type": "string",
            "title": "id"
        },
        "price": {
            "$ref": "#/definitions/RecalculatePrice",
            "title": "price"
        },
        "products": {
            "type": "array",
            "items": {
                "$ref": "#/definitions/RecalculateProduct"
            },
            "title": "products"
        },
        "loggedUser": {
            "type": "string",
            "title": "loggedUser"
        }
    },
    "required": [
        "price",
        "products",
        "requestId"
    ],
    "definitions": {
        "RecalculatePrice": {
            "title": "RecalculatePrice",
            "type": "object",
            "properties": {
                "currency": {
                    "description": "The currency",
                    "type": "string",
                    "title": "currency"
                },
                "discounts": {
                    "description": "Discounts to apply in price calculation.",
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/RecalculateDiscount"
                    },
                    "title": "discounts"
                }
            },
            "required": [
                "currency",
                "discounts"
            ]
        },
        "RecalculateDiscount": {
            "title": "RecalculateDiscount",
            "type": "object",
            "properties": {
                "code": {
                    "description": "The discount code.",
                    "maxLength": 36,
                    "type": "string",
                    "title": "code"
                }
            },
            "required": [
                "code"
            ]
        },
        "RecalculateProduct": {
            "title": "RecalculateProduct",
            "type": "object",
            "properties": {
                "ean": {
                    "description": "The ean (or other barcode on the product). Specifying this allows the user to search in his order history by scanning the barcode to do a re-purchase.",
                    "maxLength": 36,
                    "type": "string",
                    "title": "ean"
                },
                "id": {
                    "description": "The unique ID of the product.",
                    "maxLength": 36,
                    "type": "string",
                    "title": "id"
                },
                "quantity": {
                    "description": "The requested quantity",
                    "type": "number",
                    "title": "quantity"
                }
            },
            "required": [
                "id",
                "quantity"
            ]
        }
    },
    "$schema": "http://json-schema.org/draft-07/schema#"
}

Odpowiedź

W odpowiedzi, sklep przesyła zawartość koszyka oraz dostępne opcje dostawy, w takim samym formacie jak w przypadku odpowiedzi na pobieranie koszyka. Parametr expiresAt wskazuje czas do którego koszyk jest aktualny i zablokowany. Tak jak w poniższym przykładzie:

Lock basket response
{
  "id": "basket-id",
  "expiresAt": "2022-03-23T21:00:00Z",
  "price": {
    "currency": "PLN",
    "discounts": [
      {
        "code": "discount-code-text",
        "value": 1000
      }
    ],
    "basketValue": 18000
  },
  "deliveryOptions": [
    {
      "key": "INPOST_APM",
      "cost": 0
    },
    {
      "key": "DPD_COURIER",
      "cost": 1000
    }
  ],
  "products": [
    {
      "ean": "12312",
      "id": "id123",
      "name": "Superb product",
      "images": ["http://cdn.merchant.com/static/products/id123/1", "http://cdn.merchant.com/static/products/id123/2"],
      "quantity": 2,
      "unitPrice": 6000,
      "linePrice": 12000,
      "originalUnitPrice": 7000,
      "originalLinePrice": 14000,
      "error": "QUANTITY_TOO_BIG"
    }
  ],
  "loggedUser": "user-id-from-webshop"
}
Lock basket response v1
{
    "description": "Basket returned on retrieval or recalculation",
    "additionalProperties": false,
    "type": "object",
    "properties": {
        "id": {
            "description": "The unique ID of the basket.",
            "maxLength": 36,
            "type": "string",
            "title": "id"
        },
        "requestId": {
            "description": "The ID of the basket request as generated by OpenApp. Mandatory in case of an asynchronous connection through a queue in order to correlate the placed order with the response.",
            "maxLength": 36,
            "type": "string",
            "title": "requestId"
        },
        "expiresAt": {
            "description": "The moment until which the basket is valid. This can be used to indicate a basket needs to be ordered before a specific time in order to guarantee availability.",
            "format": "date-time",
            "type": "string",
            "title": "expiresAt"
        },
        "oaOrderId": {
            "description": "The OpenApp order ID (if known).",
            "type": "string",
            "title": "oaOrderId"
        },
        "price": {
            "$ref": "#/definitions/BasketPrice",
            "title": "price"
        },
        "deliveryOptions": {
            "type": "array",
            "items": {
                "$ref": "#/definitions/BasketDeliveryOption"
            },
            "title": "deliveryOptions"
        },
        "products": {
            "type": "array",
            "items": {
                "$ref": "#/definitions/BasketProduct"
            },
            "title": "products"
        },
        "invoiceAddressMandatory": {
            "description": "Whether an invoice address is mandatory",
            "type": "boolean",
            "title": "invoiceAddressMandatory"
        },
        "loggedUser": {
            "description": "The unique ID of the user.",
            "maxLength": 255,
            "type": "string",
            "title": "loggedUser"
        }
    },
    "required": [
        "deliveryOptions",
        "expiresAt",
        "id",
        "price",
        "products"
    ],
    "definitions": {
        "BasketPrice": {
            "title": "BasketPrice",
            "type": "object",
            "properties": {
                "currency": {
                    "type": "string",
                    "title": "currency"
                },
                "basketValue": {
                    "description": "The price of the basket. Price is expressed as the number (integer) of 1/100s of the price.",
                    "minimum": 0,
                    "type": "integer",
                    "title": "basketValue"
                },
                "discounts": {
                    "description": "Applied discounts",
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/BasketPriceDiscount"
                    },
                    "title": "discounts"
                }
            },
            "required": [
                "basketValue",
                "currency",
                "discounts"
            ]
        },
        "BasketPriceDiscount": {
            "title": "BasketPriceDiscount",
            "type": "object",
            "properties": {
                "code": {
                    "description": "The discount code.",
                    "maxLength": 36,
                    "type": "string",
                    "title": "code"
                },
                "value": {
                    "description": "The value of the discount. Value is expressed as the number (integer) of 1/100s of the currency.",
                    "minimum": 0,
                    "type": "integer",
                    "title": "value"
                },
                "error": {
                    "description": "The error code of the discount",
                    "enum": [
                        "EXPIRED",
                        "INVALID",
                        "NOT_APPLICABLE",
                        "USED"
                    ],
                    "type": "string",
                    "title": "error"
                }
            },
            "required": [
                "code",
                "value"
            ]
        },
        "BasketDeliveryOption": {
            "title": "BasketDeliveryOption",
            "type": "object",
            "properties": {
                "key": {
                    "$ref": "#/definitions/MerchantDeliveryOptions",
                    "title": "key"
                },
                "cost": {
                    "description": "The price of the delivery. Price is expressed as the number (integer) of 1/100s of the price.",
                    "type": "integer",
                    "title": "cost"
                },
                "timing": {
                    "description": "The estimation of the delivery time, i.e. 'next business day' or 'July 18th'.",
                    "maxLength": 40,
                    "type": "string",
                    "title": "timing"
                }
            },
            "required": [
                "cost",
                "key"
            ]
        },
        "MerchantDeliveryOptions": {
            "title": "MerchantDeliveryOptions",
            "enum": [
                "DHL_COURIER",
                "DHL_PICKUP",
                "DPD_COURIER",
                "DPD_PICKUP",
                "ELECTRONIC",
                "FEDEX_COURIER",
                "GEIS_COURIER",
                "GLS_COURIER",
                "INPOST_APM",
                "INPOST_COURIER",
                "INSTORE_PICKUP",
                "ORLEN_APM",
                "POCZTA_POLSKA_APM",
                "POCZTEX_COURIER",
                "UPS_COURIER"
            ],
            "type": "string"
        },
        "BasketProduct": {
            "title": "BasketProduct",
            "type": "object",
            "properties": {
                "name": {
                    "description": "The product name.",
                    "type": "string",
                    "title": "name"
                },
                "images": {
                    "description": "The URLs of the product image",
                    "type": "array",
                    "items": {
                        "type": "string"
                    },
                    "title": "images"
                },
                "originalUnitPrice": {
                    "description": "The original (before discount) price of a single product. Price is expressed as the number (integer) of 1/100s of the price.",
                    "type": "integer",
                    "title": "originalUnitPrice"
                },
                "originalLinePrice": {
                    "description": "The original (before discount) price of the products. Price is expressed as the number (integer) of 1/100s of the price.",
                    "type": "integer",
                    "title": "originalLinePrice"
                },
                "error": {
                    "description": "Error code when the product request can not be accomodated in a recalculate request.",
                    "enum": [
                        "OUT_OF_STOCK",
                        "QUANTITY_TOO_BIG"
                    ],
                    "type": "string",
                    "title": "error"
                },
                "ean": {
                    "description": "The ean (or other barcode on the product). Specifying this allows the user to search in his order history by scanning the barcode to do a re-purchase.",
                    "maxLength": 36,
                    "type": "string",
                    "title": "ean"
                },
                "id": {
                    "description": "The unique ID of the product.",
                    "maxLength": 36,
                    "type": "string",
                    "title": "id"
                },
                "quantity": {
                    "description": "The number of items in the purchase.",
                    "minimum": 0,
                    "type": "integer",
                    "title": "quantity"
                },
                "unitPrice": {
                    "description": "The price of a single product. Price is expressed as the number (integer) of 1/100s of the price.",
                    "type": "integer",
                    "title": "unitPrice"
                },
                "linePrice": {
                    "description": "The price of the products. Price is expressed as the number (integer) of 1/100s of the price.",
                    "type": "integer",
                    "title": "linePrice"
                }
            },
            "required": [
                "id",
                "images",
                "linePrice",
                "name",
                "originalLinePrice",
                "originalUnitPrice",
                "quantity",
                "unitPrice"
            ]
        }
    },
    "$schema": "http://json-schema.org/draft-07/schema#"
}

Jest oczywiście możliwe, że sklep nie będzie w stanie zrealizować żądanego koszyka, na przykład z powodu braku towaru lub zmiany ceny. Wówczas sklep odpowiada przesyłając część koszyka, która jest dostępna oraz kod błędu wskazujący błędy w koszyku. OpenApp wyświetli te informacje użytkownikowi, aby mógł on zdecydować o dalszym zakupie.

To jest przykład prostego koszyka. Pełen opis znajduje się tu recalculate basket JSON schema.