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 }