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