taler-docs

Documentation for GNU Taler components, APIs and protocols
Log | Files | Refs | README | LICENSE

post-private-orders.rst (20507B)


      1 .. _merchant-post-private-orders:
      2 
      3 .. http:post:: [/instances/$INSTANCE]/private/orders
      4 
      5   Create a new order that a customer can pay for.
      6 
      7   This request is **not** idempotent unless an ``order_id`` is explicitly specified.
      8   However, while repeating without an ``order_id`` will create another order, that is
      9   generally pretty harmless (as long as only one of the orders is returned to the wallet).
     10 
     11   .. note::
     12 
     13     This endpoint does not return a URL to redirect your user to confirm the
     14     payment.  To get this URL use either
     15     :http:get:`[/instances/$INSTANCE]/orders/$ORDER_ID` (with
     16     ``taler_pay_uri`` in the `StatusUnpaidResponse`), or
     17     :http:get:`[/instances/$INSTANCE]/private/orders/$ORDER_ID` with the
     18     ``taler_pay_uri`` in the `CheckPaymentUnpaidResponse`). That said,
     19     it is also possible to construct the URL by combining the base URL
     20     with the information from the `PostOrderResponse`.
     21     The API is structured this way since the payment redirect URL is not
     22     unique for every order: there might be varying parameters such as the
     23     session id.
     24 
     25   **Required permission:** ``orders-write``
     26 
     27   **Request:**
     28 
     29   The request must be a `PostOrderRequest`.
     30 
     31   **Response:**
     32 
     33   :http:statuscode:`200 OK`:
     34     The backend has successfully created the proposal.  The response is a
     35     :ts:type:`PostOrderResponse`.
     36   :http:statuscode:`400 Bad Request`:
     37     The request body is malformed.
     38     Returned with ``TALER_EC_GENERIC_PARAMETER_MALFORMED``,
     39     ``TALER_EC_GENERIC_PARAMETER_MISSING``,
     40     ``TALER_EC_GENERIC_JSON_INVALID``,
     41     ``TALER_EC_GENERIC_VERSION_MALFORMED``,
     42     ``TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_PROPOSAL_PARSE_ERROR``,
     43     ``TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_PAY_DEADLINE_IN_PAST``,
     44     ``TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_DELIVERY_DATE_IN_PAST``,
     45     ``TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_REFUND_DEADLINE_IN_PAST``,
     46     ``TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_WIRE_DEADLINE_IS_NEVER`` or
     47     ``TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_REFUND_AFTER_WIRE_DEADLINE``.
     48   :http:statuscode:`404 Not found`:
     49     Possible reasons are:
     50 
     51     (1) The order given used products from the inventory, but those were
     52         not found in the inventory.
     53     (2) The merchant instance is unknown (including possibly the instance
     54         being not configured for new orders).
     55     (3) The wire method specified is not supported by the backend.
     56     (4) An OTP device ID was specified and is unknown.
     57 
     58     Returned with ``TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN``,
     59     ``TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN``,
     60     ``TALER_EC_MERCHANT_GENERIC_MONEY_POT_UNKNOWN``,
     61     ``TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_INSTANCE_CONFIGURATION_LACKS_WIRE`` or
     62     ``TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_TOKEN_FAMILY_SLUG_UNKNOWN``.
     63   :http:statuscode:`409 Conflict`:
     64     A different proposal already exists under the specified order ID,
     65     or the requested currency is not supported by this backend.
     66     Returned with ``TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ALREADY_EXISTS``,
     67     ``TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGES_FOR_WIRE_METHOD`` or
     68     ``TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGE_FOR_CURRENCY``.
     69   :http:statuscode:`410 Gone`:
     70     The order given used products from the inventory that are out of stock.
     71     The response is a :ts:type:`OutOfStockResponse`.
     72   :http:statuscode:`451 Unavailable for Legal Reasons`:
     73     The order could not be created because of legal
     74     reasons, specifically no exchange would accept
     75     a payment at this time because we have not yet
     76     satisfied the respective legal requirements.
     77     The :ref:`KYC status <merchantkycstatus>` API
     78     can be used to determine details about how to
     79     proceed with the KYC process.
     80     Since **v25**, the body is an
     81     `OrderRefusedErrorDetailResponse` with an error
     82     code of ``TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_AMOUNT_EXCEEDS_LEGAL_LIMITS``.
     83   :http:statuscode:`500 Internal Server Error`:
     84     The server experienced an internal failure.
     85     Returned with ``TALER_EC_GENERIC_DB_STORE_FAILED``,
     86     ``TALER_EC_GENERIC_DB_FETCH_FAILED``,
     87     ``TALER_EC_GENERIC_DB_COMMIT_FAILED``,
     88     ``TALER_EC_GENERIC_DB_SOFT_FAILURE``,
     89     ``TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH``,
     90     ``TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE``,
     91     ``TALER_EC_GENERIC_ALLOCATION_FAILURE``,
     92     ``TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_LOCALTIME``,
     93     ``TALER_EC_MERCHANT_GENERIC_EXCHANGE_KEYS_FAILURE`` or
     94     ``TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH``.
     95 
     96   **Details:**
     97 
     98   .. ts:def:: PostOrderRequest
     99 
    100     interface PostOrderRequest {
    101       // The order must at least contain the minimal
    102       // order detail, but can override all.
    103       order: Order;
    104 
    105       // If set, the backend will then set the refund deadline to the
    106       // payment deadline plus the specified delay.
    107       // If it's not set, the default value of the backend might be
    108       // used. Note that both this value and the backend default
    109       // will be ignored if ``refund_deadline`` is set in ``order``
    110       // as the ``refund_deadline`` takes precedence.
    111       // A value of "forever" is not allowed.
    112       refund_delay?: RelativeTime;
    113 
    114       // Specifies the payment target preferred by the client. Can be used
    115       // to select among the various (active) wire methods supported by the instance.
    116       payment_target?: string;
    117 
    118       // The session for which the payment is made (or replayed).
    119       // Only set for session-based payments.
    120       // Since protocol **v6**.
    121       session_id?: string;
    122 
    123       // Specifies that some products are to be included in the
    124       // order from the inventory.  For these inventory management
    125       // is performed (so the products must be in stock) and
    126       // details are completed from the product data of the backend.
    127       inventory_products?: MinimalInventoryProduct[];
    128 
    129       // Specifies a lock identifier that was used to
    130       // lock a product in the inventory.  Only useful if
    131       // ``inventory_products`` is set.  Used in case a frontend
    132       // reserved quantities of the individual products while
    133       // the shopping cart was being built.  Multiple UUIDs can
    134       // be used in case different UUIDs were used for different
    135       // products (i.e. in case the user started with multiple
    136       // shopping sessions that were combined during checkout).
    137       lock_uuids?: string[];
    138 
    139       // Should a token for claiming the order be generated?
    140       // False can make sense if the ORDER_ID is sufficiently
    141       // high entropy to prevent adversarial claims (like it is
    142       // if the backend auto-generates one). Default is 'true'.
    143       // Note: This is NOT related to tokens used for subscriptins or discounts.
    144       create_token?: boolean;
    145 
    146       // OTP device ID to associate with the order.
    147       // This parameter is optional.
    148       otp_id?: string;
    149 
    150     }
    151 
    152   The `Order` object represents the starting point for new `ContractTerms`.
    153   After validating and sanatizing all inputs, the merchant backend will add
    154   additional information to the order and create a new `ContractTerms` object
    155   that will be stored in the database.
    156 
    157   .. ts:def:: Order
    158 
    159     type Order = (OrderV1 | OrderV0) & OrderCommon;
    160 
    161   .. ts:def:: OrderV1
    162 
    163     interface OrderV1 {
    164       // Version 1 order support discounts and subscriptions.
    165       // https://docs.taler.net/design-documents/046-mumimo-contracts.html
    166       // @since protocol **v21**
    167       version: 1;
    168 
    169       // List of contract choices that the customer can select from.
    170       // @since protocol **v21**
    171       choices?: OrderChoice[];
    172     }
    173 
    174   .. ts:def:: OrderV0
    175 
    176     interface OrderV0 {
    177       // Optional, defaults to 0 if not set.
    178       version?: 0;
    179 
    180       // Total price for the transaction, including tip. The exchange will
    181       // subtract deposit fees from that amount before transferring it to
    182       // the merchant.
    183       amount: Amount;
    184 
    185       // Optional tip amount. Must match the currency of ``amount``.
    186       // Since protocol **v25**.
    187       tip?: Amount;
    188 
    189       // Maximum total deposit fee accepted by the merchant for this contract.
    190       // Overrides defaults of the merchant instance.
    191       max_fee?: Amount;
    192     }
    193 
    194   .. ts:def:: OrderCommon
    195 
    196     interface OrderCommon {
    197       // Human-readable description of the whole purchase.
    198       summary: string;
    199 
    200       // Map from IETF BCP 47 language tags to localized summaries.
    201       summary_i18n?: { [lang_tag: string]: string };
    202 
    203       // Unique identifier for the order. Only characters
    204       // allowed are "A-Za-z0-9" and ".:_-".
    205       // Must be unique within a merchant instance.
    206       // For merchants that do not store proposals in their DB
    207       // before the customer paid for them, the ``order_id`` can be used
    208       // by the frontend to restore a proposal from the information
    209       // encoded in it (such as a short product identifier and timestamp).
    210       order_id?: string;
    211 
    212       // URL where the same contract could be ordered again (if
    213       // available). Returned also at the public order endpoint
    214       // for people other than the actual buyer (hence public,
    215       // in case order IDs are guessable).
    216       public_reorder_url?: string;
    217 
    218       // See documentation of ``fulfillment_url`` field in `ContractTerms`.
    219       // Either fulfillment_url or fulfillment_message must be specified.
    220       // When creating an order, the fulfillment URL can
    221       // contain ``${ORDER_ID}`` which will be substituted with the
    222       // order ID of the newly created order.
    223       fulfillment_url?: string;
    224 
    225       // See documentation of ``fulfillment_message`` in `ContractTerms`.
    226       // Either ``fulfillment_url`` or ``fulfillment_message`` must be specified.
    227       fulfillment_message?: string;
    228 
    229       // Map from IETF BCP 47 language tags to localized fulfillment
    230       // messages.
    231       fulfillment_message_i18n?: { [lang_tag: string]: string };
    232 
    233       // Minimum age the buyer must have to buy.
    234       minimum_age?: Integer;
    235 
    236       // List of products that are part of the purchase.
    237       products?: ProductSold[];
    238 
    239       // Time when this contract was generated. If null, defaults to current
    240       // time of merchant backend.
    241       timestamp?: Timestamp;
    242 
    243       // After this deadline has passed, no refunds will be accepted.
    244       // Overrides deadline calculated from ``refund_delay`` in
    245       // ``PostOrderRequest``.
    246       // A value of "never" is not allowed.
    247       refund_deadline?: Timestamp;
    248 
    249       // After this deadline, the merchant won't accept payments for the contract.
    250       // Overrides deadline calculated from default pay delay configured in
    251       // merchant backend.
    252       // A value of "never" is not allowed.
    253       pay_deadline?: Timestamp;
    254 
    255       // Transfer deadline for the exchange. Must be in the deposit permissions
    256       // of coins used to pay for this order.
    257       // Overrides deadline calculated from default wire transfer delay
    258       // configured in merchant backend. Must be after refund deadline.
    259       // A value of "never" is not allowed.
    260       wire_transfer_deadline?: Timestamp;
    261 
    262       // Base URL of the (public!) merchant backend API.
    263       // Must be an absolute URL that ends with a slash.
    264       // Defaults to the base URL this request was made to.
    265       merchant_base_url?: string;
    266 
    267       // Delivery location for (all!) products.
    268       delivery_location?: Location;
    269 
    270       // Time indicating when the order should be delivered.
    271       // May be overwritten by individual products.
    272       // Must be in the future.
    273       delivery_date?: Timestamp;
    274 
    275       // See documentation of ``auto_refund`` in ``ContractTerms``.
    276       // Specifies for how long the wallet should try to get an
    277       // automatic refund for the purchase.
    278       auto_refund?: RelativeTime;
    279 
    280       // Extra data that is only interpreted by the merchant frontend.
    281       // Useful when the merchant needs to store extra information on a
    282       // contract without storing it separately in their database.
    283       // Must really be an Object (not a string, integer, float or array).
    284       extra?: Object;
    285 
    286       // Money pot to increment for whatever order payment amount
    287       // is not yet assigned to a pot via the ``ProductSold``.
    288       // Not useful to wallets, only for
    289       // merchant-internal accounting.
    290       // Since protocol **v25**.
    291       order_default_money_pot?: Integer;
    292 
    293     }
    294 
    295 
    296   The `OrderChoice` object describes a possible choice within an order. The
    297   choice is done by the wallet and consists of in- and outputs. In the example
    298   of buying an article, the merchant could present the customer with the
    299   choice to use a valid subscription token or pay using a gift
    300   voucher. Available since protocol **v21**.
    301 
    302   .. ts:def:: OrderChoice
    303 
    304     interface OrderChoice {
    305       // Total price for the choice. The exchange will subtract deposit
    306       // fees from that amount before transferring it to the merchant.
    307       amount: Amount;
    308 
    309       // Optional tip amount. Must match the currency of ``amount``.
    310       // Since protocol **v25**.
    311       tip?: Amount;
    312 
    313       // Human readable description of the semantics of the choice
    314       // within the contract to be shown to the user at payment.
    315       description?: string;
    316 
    317       // Map from IETF 47 language tags to localized descriptions.
    318       description_i18n?: { [lang_tag: string]: string };
    319 
    320       // Inputs that must be provided by the customer, if this choice is selected.
    321       // Defaults to empty array if not specified.
    322       inputs?: OrderInput[];
    323 
    324       // Outputs provided by the merchant, if this choice is selected.
    325       // Defaults to empty array if not specified.
    326       outputs?: OrderOutput[];
    327 
    328       // Maximum total deposit fee accepted by the merchant for this contract.
    329       // Overrides defaults of the merchant instance.
    330       max_fee?: Amount;
    331     }
    332 
    333   .. ts:def:: OrderInput
    334 
    335     // For now, only token inputs are supported.
    336     type OrderInput = OrderInputToken;
    337 
    338   .. ts:def:: OrderInputToken
    339 
    340     interface OrderInputToken {
    341 
    342       // Token input.
    343       type: "token";
    344 
    345       // Token family slug as configured in the merchant backend. Slug is unique
    346       // across all configured tokens of a merchant.
    347       token_family_slug: string;
    348 
    349       // How many units of the input are required.
    350       // Defaults to 1 if not specified. Output with count == 0 are ignored by
    351       // the merchant backend.
    352       count?: Integer;
    353 
    354     }
    355 
    356   .. ts:def:: OrderOutput
    357 
    358     type OrderOutput = OrderOutputToken | OrderOutputTaxReceipt;
    359 
    360   .. ts:def:: OrderOutputToken
    361 
    362     interface OrderOutputToken {
    363 
    364       // Token output.
    365       type: "token";
    366 
    367       // Token family slug as configured in the merchant backend. Slug is unique
    368       // across all configured tokens of a merchant.
    369       token_family_slug: string;
    370 
    371       // How many units of the output are issued by the merchant.
    372       // Defaults to 1 if not specified. Output with count == 0 are ignored by
    373       // the merchant backend.
    374       count?: Integer;
    375 
    376       // When should the output token be valid. Can be specified if the
    377       // desired validity period should be in the future (like selling
    378       // a subscription for the next month). Optional. If not given,
    379       // the validity is supposed to be "now" (time of order creation).
    380       valid_at?: Timestamp;
    381 
    382     }
    383 
    384   .. ts:def:: OrderOutputTaxReceipt
    385 
    386     interface OrderOutputTaxReceipt {
    387 
    388       // Tax receipt output.
    389       type: "tax-receipt";
    390 
    391     }
    392 
    393   The following `MinimalInventoryProduct` can be provided if the parts of the
    394   order are inventory-based, that is if the `PostOrderRequest` uses
    395   ``inventory_products``. For such products, which must be in the backend's inventory,
    396   the backend can automatically fill in the amount and other details about
    397   the product that are known to it from its ``products`` table.
    398   Note that the ``inventory_products`` will be appended to the
    399   list of ``products`` that the frontend already put into the ``order``.
    400   So if the frontend can sell additional non-inventory products together
    401   with ``inventory_products``.  Note that the backend will NOT update
    402   the ``amount`` of the ``order``, so the frontend must already have calculated
    403   the total price --- including the ``inventory_products``.
    404 
    405   .. ts:def:: MinimalInventoryProduct
    406 
    407     // Note that if the frontend does give details beyond these,
    408     // it will override those details (including price or taxes)
    409     // that the backend would otherwise fill in via the inventory.
    410     interface MinimalInventoryProduct {
    411 
    412       // Which product is requested (here mandatory!).
    413       product_id: string;
    414 
    415       // Legacy integer quantity.
    416       // Deprecated since **v25**;
    417       // defaults to 1 if both ``quantity`` and ``unit_quantity`` are absent.
    418       quantity?: Integer;
    419 
    420       // Preferred quantity string using "<integer>[.<fraction>]" syntax.
    421       // @since **v25**;
    422       unit_quantity?: string
    423 
    424       // Money pot to use for this product, overrides value from
    425       // the inventory if given.
    426       // Since **v25**.
    427       product_money_pot?: Integer;
    428 
    429     }
    430 
    431   Supply either ``quantity`` or ``unit_quantity`` when referencing inventory products. If both are
    432   missing the backend assumes a quantity of one. ``unit_quantity`` follows the same decimal-string
    433   rules as ``unit_total_stock``.
    434 
    435   .. ts:def:: PostOrderResponse
    436 
    437     interface PostOrderResponse {
    438       // Order ID of the response that was just created.
    439       order_id: string;
    440 
    441       // Deadline when the offer expires; the customer must pay before.
    442       // @since protocol **v21**.
    443       pay_deadline: Timestamp;
    444 
    445       // Token that authorizes the wallet to claim the order.
    446       // Provided only if "create_token" was set to 'true'
    447       // in the request.
    448       token?: ClaimToken;
    449     }
    450 
    451   .. ts:def:: OutOfStockResponse
    452 
    453     interface OutOfStockResponse {
    454 
    455       // Product ID of an out-of-stock item.
    456       product_id: string;
    457 
    458       // Legacy integer quantity requested. Deprecated; see ``unit_requested_quantity``.
    459       requested_quantity: Integer;
    460 
    461       // Requested quantity using "<integer>[.<fraction>]" syntax with up to six fractional digits.
    462       unit_requested_quantity: string;
    463 
    464       // Legacy integer availability (must be below ``requested_quantity``).
    465       available_quantity: Integer;
    466 
    467       // Available quantity using "<integer>[.<fraction>]" syntax with up to six fractional digits.
    468       unit_available_quantity: string;
    469 
    470       // When do we expect the product to be again in stock?
    471       // Optional, not given if unknown.
    472       restock_expected?: Timestamp;
    473     }
    474 
    475 
    476   .. ts:def:: OrderRefusedErrorDetailResponse
    477 
    478     interface OrderRefusedErrorDetailResponse {
    479 
    480       // Numeric `error code <error-codes>` unique to the condition.
    481       // Will be MERCHANT_PRIVATE_POST_ORDERS_AMOUNT_EXCEEDS_LEGAL_LIMITS).
    482       code: ErrorCode;
    483 
    484       // Human-readable description of the error, i.e. "missing parameter", "commitment violation", ...
    485       // Should give a human-readable hint about the error's nature. Optional, may change without notice!
    486       hint?: string;
    487 
    488       // Detail about why a specific exchange was rejected.
    489       // Note that an exchange that was allowed is not listed.
    490       // It is possible that no exchanges were rejected (in which
    491       // case this array would be empty) and still the operation
    492       // failed because the total of the allowed amounts per
    493       // exchange ended up below the order total. Thus, that
    494       // is ultimately always the cause here (as per the code),
    495       // but the *other* reasons why exchanges might have been
    496       // rejected could be enlightening to the user and are
    497       // thus provided here.
    498       exchange_rejections: ExchangeRejectionDetail;
    499     }
    500 
    501   .. ts:def:: ExchangeRejectionDetail
    502 
    503     interface ExchangeRejectionDetail {
    504 
    505       // Base URL of the rejected exchange
    506       exchange_url: string;
    507 
    508       // Numeric `error code <error-codes>` unique to why
    509       // this exchange was not acceptable.
    510       // Can be MERCHANT_GENERIC_CURRENCY_MISMATCH,
    511       // MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_LEGALLY_REFUSED
    512       // (zero deposit limit, likely KYC required),
    513       // MERCHANT_GENERIC_EXCHANGE_KEYS_FAILURE
    514       // (we failed to download /keys from the exchange),
    515       // MERCHANT_POST_ORDERS_ID_PAY_WIRE_METHOD_UNSUPPORTED
    516       // (none of our bank accounts has a compatible wire method)
    517       code: ErrorCode;
    518 
    519       // Human-readable description of the error.
    520       // Should give a human-readable hint about the error's nature.
    521       // Optional, may change without notice!
    522       hint?: string;
    523 
    524     }