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