taler-merchant-httpd_get-private-orders-ORDER_ID.c (60321B)
1 /* 2 This file is part of TALER 3 (C) 2017-2024, 2026 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file src/backend/taler-merchant-httpd_get-private-orders-ORDER_ID.c 18 * @brief implementation of GET /private/orders/ID handler 19 * @author Florian Dold 20 * @author Christian Grothoff 21 * @author Bohdan Potuzhnyi 22 * @author Iván Ávalos 23 */ 24 #include "platform.h" 25 #include <taler/taler_json_lib.h> 26 #include <taler/taler_dbevents.h> 27 #include <taler/taler_error_codes.h> 28 #include <taler/taler_util.h> 29 #include <gnunet/gnunet_common.h> 30 #include <gnunet/gnunet_json_lib.h> 31 #include "taler/taler_merchant_util.h" 32 #include "taler-merchant-httpd_helper.h" 33 #include "taler-merchant-httpd_get-private-orders.h" 34 #include "taler-merchant-httpd_get-private-orders-ORDER_ID.h" 35 #include "merchant-database/get_contract_terms_status.h" 36 #include "merchant-database/iterate_deposits_by_order.h" 37 #include "merchant-database/get_order.h" 38 #include "merchant-database/get_order_by_fulfillment.h" 39 #include "merchant-database/iterate_refunds_detailed.h" 40 #include "merchant-database/iterate_transfer_details_by_order.h" 41 #include "merchant-database/update_to_contract_terms_wired.h" 42 #include "merchant-database/preflight.h" 43 #include "merchant-database/event_listen.h" 44 45 /** 46 * Data structure we keep for a check payment request. 47 */ 48 struct GetOrderRequestContext; 49 50 51 /** 52 * Request to an exchange for details about wire transfers 53 * in response to a coin's deposit operation. 54 */ 55 struct TransferQuery 56 { 57 58 /** 59 * Kept in a DLL. 60 */ 61 struct TransferQuery *next; 62 63 /** 64 * Kept in a DLL. 65 */ 66 struct TransferQuery *prev; 67 68 /** 69 * Base URL of the exchange. 70 */ 71 char *exchange_url; 72 73 /** 74 * Overall request this TQ belongs with. 75 */ 76 struct GetOrderRequestContext *gorc; 77 78 /** 79 * Hash of the merchant's bank account the transfer (presumably) went to. 80 */ 81 struct TALER_MerchantWireHashP h_wire; 82 83 /** 84 * Value deposited (including deposit fee). 85 */ 86 struct TALER_Amount amount_with_fee; 87 88 /** 89 * Deposit fee paid for this coin. 90 */ 91 struct TALER_Amount deposit_fee; 92 93 /** 94 * Public key of the coin this is about. 95 */ 96 struct TALER_CoinSpendPublicKeyP coin_pub; 97 98 /** 99 * Which deposit operation is this about? 100 */ 101 uint64_t deposit_serial; 102 103 }; 104 105 106 /** 107 * Phases of order processing. 108 */ 109 enum GetOrderPhase 110 { 111 /** 112 * Initialization. 113 */ 114 GOP_INIT = 0, 115 116 /** 117 * Obtain contract terms from database. 118 */ 119 GOP_FETCH_CONTRACT = 1, 120 121 /** 122 * Parse the contract terms. 123 */ 124 GOP_PARSE_CONTRACT = 2, 125 126 /** 127 * Check if the contract was fully paid. 128 */ 129 GOP_CHECK_PAID = 3, 130 131 /** 132 * Check if the wallet may have purchased an equivalent 133 * order before and we need to redirect the wallet to 134 * an existing paid order. 135 */ 136 GOP_CHECK_REPURCHASE = 4, 137 138 /** 139 * Terminate processing of unpaid orders, either by 140 * suspending until payment or by returning the 141 * unpaid order status. 142 */ 143 GOP_UNPAID_FINISH = 5, 144 145 /** 146 * Load all deposits associated with the order. 147 */ 148 GOP_CHECK_DEPOSITS = 6, 149 150 /** 151 * Check if the (paid) order was refunded. 152 */ 153 GOP_CHECK_REFUNDS = 7, 154 155 /** 156 * Check local records for transfers of funds to 157 * the merchant. 158 */ 159 GOP_CHECK_LOCAL_TRANSFERS = 8, 160 161 /** 162 * Generate final comprehensive result. 163 */ 164 GOP_REPLY_RESULT = 9, 165 166 /** 167 * End with the HTTP status and error code in 168 * wire_hc and wire_ec. 169 */ 170 GOP_ERROR = 10, 171 172 /** 173 * We are suspended awaiting payment. 174 */ 175 GOP_SUSPENDED_ON_UNPAID = 11, 176 177 /** 178 * Processing is done, return #MHD_YES. 179 */ 180 GOP_END_YES = 12, 181 182 /** 183 * Processing is done, return #MHD_NO. 184 */ 185 GOP_END_NO = 13 186 187 }; 188 189 190 /** 191 * Data structure we keep for a check payment request. 192 */ 193 struct GetOrderRequestContext 194 { 195 196 /** 197 * Processing phase we are in. 198 */ 199 enum GetOrderPhase phase; 200 201 /** 202 * Entry in the #resume_timeout_heap for this check payment, if we are 203 * suspended. 204 */ 205 struct TMH_SuspendedConnection sc; 206 207 /** 208 * Which merchant instance is this for? 209 */ 210 struct TMH_HandlerContext *hc; 211 212 /** 213 * session of the client 214 */ 215 const char *session_id; 216 217 /** 218 * Kept in a DLL while suspended on exchange. 219 */ 220 struct GetOrderRequestContext *next; 221 222 /** 223 * Kept in a DLL while suspended on exchange. 224 */ 225 struct GetOrderRequestContext *prev; 226 227 /** 228 * Head of DLL of individual queries for transfer data. 229 */ 230 struct TransferQuery *tq_head; 231 232 /** 233 * Tail of DLL of individual queries for transfer data. 234 */ 235 struct TransferQuery *tq_tail; 236 237 /** 238 * Timeout task while waiting on exchange. 239 */ 240 struct GNUNET_SCHEDULER_Task *tt; 241 242 /** 243 * Database event we are waiting on to be resuming 244 * for payment or refunds. 245 */ 246 struct GNUNET_DB_EventHandler *eh; 247 248 /** 249 * Database event we are waiting on to be resuming 250 * for session capture. 251 */ 252 struct GNUNET_DB_EventHandler *session_eh; 253 254 /** 255 * Contract terms of the payment we are checking. NULL when they 256 * are not (yet) known. 257 */ 258 json_t *contract_terms_json; 259 260 /** 261 * Parsed contract terms, NULL when parsing failed 262 */ 263 struct TALER_MERCHANT_Contract *contract_terms; 264 265 /** 266 * Proto-contract. Be careful: do NOT free this 267 * if @e contract_terms is not NULL! 268 */ 269 struct TALER_MERCHANT_ProtoContract *pc; 270 271 /** 272 * Order terms of the payment we are checking. NULL when we have 273 * a contract. 274 */ 275 json_t *order_json; 276 277 /** 278 * Parsed order details, NULL when parsing failed or we have a contract. 279 */ 280 struct TALER_MERCHANT_Order *order; 281 282 /** 283 * Common terms of @e order and @e contract. 284 */ 285 const struct TALER_MERCHANT_ContractBaseTerms *ct; 286 287 /** 288 * Timestamp of the contract or order. 289 */ 290 struct GNUNET_TIME_Timestamp timestamp; 291 292 /** 293 * Claim token of the order. 294 */ 295 struct TALER_ClaimTokenP claim_token; 296 297 /** 298 * Timestamp of the last payment. 299 */ 300 struct GNUNET_TIME_Timestamp last_payment; 301 302 /** 303 * Wire details for the payment, to be returned in the reply. NULL 304 * if not available. 305 */ 306 json_t *wire_details; 307 308 /** 309 * Details about refunds, NULL if there are no refunds. 310 */ 311 json_t *refund_details; 312 313 /** 314 * Amount of the order, unset for unpaid v1 orders. 315 */ 316 struct TALER_Amount contract_amount; 317 318 /** 319 * Hash over the @e contract_terms. 320 */ 321 struct TALER_PrivateContractHashP h_contract_terms; 322 323 /** 324 * Set to the Etag of a response already known to the 325 * client. We should only return from long-polling 326 * on timeout (with "Not Modified") or when the Etag 327 * of the response differs from what is given here. 328 * Only set if @a have_lp_not_etag is true. 329 * Set from "lp_etag" query parameter. 330 */ 331 struct GNUNET_ShortHashCode lp_not_etag; 332 333 /** 334 * Total amount the exchange deposited into our bank account 335 * (confirmed or unconfirmed), excluding fees. 336 */ 337 struct TALER_Amount deposits_total; 338 339 /** 340 * Total amount in deposit fees we paid for all coins. 341 */ 342 struct TALER_Amount deposit_fees_total; 343 344 /** 345 * Total amount in deposit fees cancelled due to refunds for all coins. 346 */ 347 struct TALER_Amount deposit_fees_refunded_total; 348 349 /** 350 * Total value of the coins that the exchange deposited into our bank 351 * account (confirmed or unconfirmed), including deposit fees. 352 */ 353 struct TALER_Amount value_total; 354 355 /** 356 * Serial ID of the order. 357 */ 358 uint64_t order_serial; 359 360 /** 361 * Index of selected choice from ``choices`` array in the contract_terms. 362 * Is -1 for orders without choices. 363 */ 364 int16_t choice_index; 365 366 /** 367 * Total refunds granted for this payment. Only initialized 368 * if @e refunded is set to true. 369 */ 370 struct TALER_Amount refund_amount; 371 372 /** 373 * Exchange HTTP error code encountered while trying to determine wire transfer 374 * details. #TALER_EC_NONE for no error encountered. 375 */ 376 unsigned int exchange_hc; 377 378 /** 379 * Exchange error code encountered while trying to determine wire transfer 380 * details. #TALER_EC_NONE for no error encountered. 381 */ 382 enum TALER_ErrorCode exchange_ec; 383 384 /** 385 * Error code encountered while trying to determine wire transfer 386 * details. #TALER_EC_NONE for no error encountered. 387 */ 388 enum TALER_ErrorCode wire_ec; 389 390 /** 391 * Set to YES if refunded orders should be included when 392 * doing repurchase detection. 393 */ 394 enum TALER_EXCHANGE_YesNoAll allow_refunded_for_repurchase; 395 396 /** 397 * HTTP status to return with @e wire_ec, 0 if @e wire_ec is #TALER_EC_NONE. 398 */ 399 unsigned int wire_hc; 400 401 /** 402 * Did we suspend @a connection and are thus in 403 * the #gorc_head DLL (#GNUNET_YES). Set to 404 * #GNUNET_NO if we are not suspended, and to 405 * #GNUNET_SYSERR if we should close the connection 406 * without a response due to shutdown. 407 */ 408 enum GNUNET_GenericReturnValue suspended; 409 410 /** 411 * Set to true if this payment has been refunded and 412 * @e refund_amount is initialized. 413 */ 414 bool refunded; 415 416 /** 417 * True if @e lp_not_etag was given. 418 */ 419 bool have_lp_not_etag; 420 421 /** 422 * True if the order was paid. 423 */ 424 bool paid; 425 426 /** 427 * True if the paid session in the database matches 428 * our @e session_id. 429 */ 430 bool paid_session_matches; 431 432 /** 433 * When we install the @e session_eh listener, we already checked 434 * the order status. We could not do it earlier, because the 435 * fulfillment URL was unavailable. However, this leaves a chance 436 * for a payment event to be missed if it happens before the first 437 * order status check and us installing the event listener. Thus, 438 * if we afterwards suspend, we should once *immediately* check the 439 * order status a 2nd time before we really suspend. This flag is 440 * set to true to handle this case. 441 */ 442 bool instant_retry; 443 444 /** 445 * True if the exchange wired the money to the merchant. 446 */ 447 bool wired; 448 449 /** 450 * True if the order remains unclaimed. 451 */ 452 bool order_only; 453 454 /** 455 * Set to true if this payment has been refunded and 456 * some refunds remain to be picked up by the wallet. 457 */ 458 bool refund_pending; 459 460 /** 461 * Set to true if our database (incorrectly) has refunds 462 * in a different currency than the currency of the 463 * original payment for the order. 464 */ 465 bool refund_currency_mismatch; 466 467 /** 468 * Set to true if our database (incorrectly) has deposits 469 * in a different currency than the currency of the 470 * original payment for the order. 471 */ 472 bool deposit_currency_mismatch; 473 }; 474 475 476 /** 477 * Head of list of suspended requests waiting on the exchange. 478 */ 479 static struct GetOrderRequestContext *gorc_head; 480 481 /** 482 * Tail of list of suspended requests waiting on the exchange. 483 */ 484 static struct GetOrderRequestContext *gorc_tail; 485 486 487 void 488 TMH_force_gorc_resume (void) 489 { 490 struct GetOrderRequestContext *gorc; 491 492 while (NULL != (gorc = gorc_head)) 493 { 494 GNUNET_CONTAINER_DLL_remove (gorc_head, 495 gorc_tail, 496 gorc); 497 GNUNET_assert (GNUNET_YES == gorc->suspended); 498 gorc->suspended = GNUNET_SYSERR; 499 MHD_resume_connection (gorc->sc.con); 500 } 501 } 502 503 504 /** 505 * We have received a trigger from the database 506 * that we should (possibly) resume the request. 507 * 508 * @param cls a `struct GetOrderRequestContext` to resume 509 * @param extra string encoding refund amount (or NULL) 510 * @param extra_size number of bytes in @a extra 511 */ 512 static void 513 resume_by_event (void *cls, 514 const void *extra, 515 size_t extra_size) 516 { 517 struct GetOrderRequestContext *gorc = cls; 518 519 (void) extra; 520 (void) extra_size; 521 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 522 "Resuming request for order %s by trigger\n", 523 gorc->hc->infix); 524 if (GNUNET_NO == gorc->suspended) 525 return; /* duplicate event is possible */ 526 gorc->suspended = GNUNET_NO; 527 gorc->phase = GOP_FETCH_CONTRACT; 528 GNUNET_CONTAINER_DLL_remove (gorc_head, 529 gorc_tail, 530 gorc); 531 MHD_resume_connection (gorc->sc.con); 532 TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */ 533 } 534 535 536 /** 537 * Clean up the session state for a GET /private/order/ID request. 538 * 539 * @param cls closure, must be a `struct GetOrderRequestContext *` 540 */ 541 static void 542 gorc_cleanup (void *cls) 543 { 544 struct GetOrderRequestContext *gorc = cls; 545 struct TransferQuery *tq; 546 547 while (NULL != (tq = gorc->tq_head)) 548 { 549 GNUNET_CONTAINER_DLL_remove (gorc->tq_head, 550 gorc->tq_tail, 551 tq); 552 GNUNET_free (tq->exchange_url); 553 GNUNET_free (tq); 554 } 555 556 if (NULL != gorc->contract_terms_json) 557 { 558 json_decref (gorc->contract_terms_json); 559 gorc->contract_terms_json = NULL; 560 } 561 if (NULL != gorc->order_json) 562 { 563 json_decref (gorc->order_json); 564 gorc->order_json = NULL; 565 } 566 if (NULL != gorc->contract_terms) 567 { 568 TALER_MERCHANT_contract_free (gorc->contract_terms); 569 gorc->contract_terms = NULL; 570 gorc->pc = NULL; /* was an alias! */ 571 } 572 if (NULL != gorc->pc) 573 { 574 TALER_MERCHANT_proto_contract_free (gorc->pc); 575 gorc->pc = NULL; 576 } 577 if (NULL != gorc->order) 578 { 579 TALER_MERCHANT_order_free (gorc->order); 580 gorc->order = NULL; 581 } 582 gorc->ct = NULL; /* avoid dangling pointer */ 583 if (NULL != gorc->wire_details) 584 json_decref (gorc->wire_details); 585 if (NULL != gorc->refund_details) 586 json_decref (gorc->refund_details); 587 if (NULL != gorc->tt) 588 { 589 GNUNET_SCHEDULER_cancel (gorc->tt); 590 gorc->tt = NULL; 591 } 592 if (NULL != gorc->eh) 593 { 594 TALER_MERCHANTDB_event_listen_cancel (gorc->eh); 595 gorc->eh = NULL; 596 } 597 if (NULL != gorc->session_eh) 598 { 599 TALER_MERCHANTDB_event_listen_cancel (gorc->session_eh); 600 gorc->session_eh = NULL; 601 } 602 GNUNET_free (gorc); 603 } 604 605 606 /** 607 * Processing the request @a gorc is finished, set the 608 * final return value in phase based on @a mret. 609 * 610 * @param[in,out] gorc order context to initialize 611 * @param mret MHD HTTP response status to return 612 */ 613 static void 614 phase_end (struct GetOrderRequestContext *gorc, 615 enum MHD_Result mret) 616 { 617 gorc->phase = (MHD_YES == mret) 618 ? GOP_END_YES 619 : GOP_END_NO; 620 } 621 622 623 /** 624 * Initialize event callbacks for the order processing. 625 * 626 * @param[in,out] gorc order context to initialize 627 */ 628 static void 629 phase_init (struct GetOrderRequestContext *gorc) 630 { 631 struct TMH_HandlerContext *hc = gorc->hc; 632 struct TMH_OrderPayEventP pay_eh = { 633 .header.size = htons (sizeof (pay_eh)), 634 .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_STATUS_CHANGED), 635 .merchant_pub = hc->instance->merchant_pub 636 }; 637 638 if (! GNUNET_TIME_absolute_is_future (gorc->sc.long_poll_timeout)) 639 { 640 gorc->phase++; 641 return; 642 } 643 644 GNUNET_CRYPTO_hash (hc->infix, 645 strlen (hc->infix), 646 &pay_eh.h_order_id); 647 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 648 "Subscribing to payment triggers for %p\n", 649 gorc); 650 gorc->eh = TALER_MERCHANTDB_event_listen ( 651 TMH_db, 652 &pay_eh.header, 653 GNUNET_TIME_absolute_get_remaining (gorc->sc.long_poll_timeout), 654 &resume_by_event, 655 gorc); 656 gorc->phase++; 657 } 658 659 660 /** 661 * Obtain latest contract terms from the database. 662 * 663 * @param[in,out] gorc order context to update 664 */ 665 static void 666 phase_fetch_contract (struct GetOrderRequestContext *gorc) 667 { 668 struct TMH_HandlerContext *hc = gorc->hc; 669 enum GNUNET_DB_QueryStatus qs; 670 671 if (NULL != gorc->contract_terms_json) 672 { 673 /* Free memory filled with old contract terms before fetching the latest 674 ones from the DB. Note that we cannot simply skip the database 675 interaction as the contract terms loaded previously might be from an 676 earlier *unclaimed* order state (which we loaded in a previous 677 invocation of this function and we are back here due to long polling) 678 and thus the contract terms could have changed during claiming. Thus, 679 we need to fetch the latest contract terms from the DB again. */ 680 json_decref (gorc->contract_terms_json); 681 gorc->contract_terms_json = NULL; 682 gorc->order_only = false; 683 } 684 TALER_MERCHANTDB_preflight (TMH_db); 685 qs = TALER_MERCHANTDB_get_contract_terms_status (TMH_db, 686 hc->instance->settings.id, 687 hc->infix, 688 gorc->session_id, 689 &gorc->contract_terms_json, 690 &gorc->order_serial, 691 &gorc->paid, 692 &gorc->wired, 693 &gorc->paid_session_matches, 694 &gorc->claim_token, 695 &gorc->choice_index); 696 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 697 "get_contract_terms (%s) returned %d\n", 698 hc->infix, 699 (int) qs); 700 if (0 > qs) 701 { 702 /* single, read-only SQL statements should never cause 703 serialization problems */ 704 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); 705 /* Always report on hard error as well to enable diagnostics */ 706 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); 707 phase_end (gorc, 708 TALER_MHD_reply_with_error (gorc->sc.con, 709 MHD_HTTP_INTERNAL_SERVER_ERROR, 710 TALER_EC_GENERIC_DB_FETCH_FAILED, 711 "contract terms")); 712 return; 713 } 714 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) 715 { 716 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 717 "Order %s is %s (%s) according to database, choice %d\n", 718 hc->infix, 719 gorc->paid ? "paid" : "unpaid", 720 gorc->wired ? "wired" : "unwired", 721 (int) gorc->choice_index); 722 gorc->phase++; 723 return; 724 } 725 GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs); 726 GNUNET_assert (! gorc->paid); 727 /* No contract, only order, fetch from orders table */ 728 gorc->order_only = true; 729 { 730 struct TALER_MerchantPostDataHashP unused; 731 732 /* We need the order for two cases: Either when the contract doesn't exist yet, 733 * or when the order is claimed but unpaid, and we need the claim token. */ 734 qs = TALER_MERCHANTDB_get_order (TMH_db, 735 hc->instance->settings.id, 736 hc->infix, 737 &gorc->claim_token, 738 &unused, 739 &gorc->contract_terms_json); 740 } 741 if (0 > qs) 742 { 743 /* single, read-only SQL statements should never cause 744 serialization problems */ 745 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); 746 /* Always report on hard error as well to enable diagnostics */ 747 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); 748 phase_end (gorc, 749 TALER_MHD_reply_with_error (gorc->sc.con, 750 MHD_HTTP_INTERNAL_SERVER_ERROR, 751 TALER_EC_GENERIC_DB_FETCH_FAILED, 752 "order")); 753 return; 754 } 755 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 756 { 757 phase_end (gorc, 758 TALER_MHD_reply_with_error (gorc->sc.con, 759 MHD_HTTP_NOT_FOUND, 760 TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN, 761 hc->infix)); 762 return; 763 } 764 gorc->phase++; 765 } 766 767 768 /** 769 * Obtain parse contract terms of the order. Extracts the fulfillment URL, 770 * total amount, summary and timestamp from the contract terms! 771 * 772 * @param[in,out] gorc order context to update 773 */ 774 static void 775 phase_parse_contract (struct GetOrderRequestContext *gorc) 776 { 777 struct TMH_HandlerContext *hc = gorc->hc; 778 779 if ( (NULL == gorc->order) && 780 (NULL != gorc->order_json) ) 781 { 782 gorc->order = TALER_MERCHANT_order_parse ( 783 gorc->order_json); 784 785 if (NULL == gorc->order) 786 { 787 GNUNET_break (0); 788 phase_end (gorc, 789 TALER_MHD_reply_with_error ( 790 gorc->sc.con, 791 MHD_HTTP_INTERNAL_SERVER_ERROR, 792 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 793 hc->infix)); 794 return; 795 } 796 gorc->ct = gorc->order->base; 797 gorc->timestamp = gorc->order->timestamp; 798 } 799 if ( (NULL == gorc->contract_terms) && 800 (NULL != gorc->contract_terms_json) ) 801 { 802 if (NULL == 803 json_object_get (gorc->contract_terms_json, 804 "nonce")) 805 { 806 /* only have a proto contract */ 807 gorc->pc = TALER_MERCHANT_proto_contract_parse ( 808 gorc->contract_terms_json); 809 810 if (NULL == gorc->pc) 811 { 812 GNUNET_break (0); 813 phase_end (gorc, 814 TALER_MHD_reply_with_error ( 815 gorc->sc.con, 816 MHD_HTTP_INTERNAL_SERVER_ERROR, 817 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 818 hc->infix)); 819 return; 820 } 821 gorc->ct = gorc->pc->base; 822 gorc->timestamp = gorc->pc->timestamp; 823 } 824 else 825 { 826 gorc->contract_terms = TALER_MERCHANT_contract_parse ( 827 gorc->contract_terms_json); 828 if (NULL == gorc->contract_terms) 829 { 830 GNUNET_break (0); 831 phase_end (gorc, 832 TALER_MHD_reply_with_error ( 833 gorc->sc.con, 834 MHD_HTTP_INTERNAL_SERVER_ERROR, 835 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 836 hc->infix)); 837 return; 838 } 839 gorc->pc = gorc->contract_terms->pc; 840 gorc->ct = gorc->pc->base; 841 gorc->timestamp = gorc->contract_terms->pc->timestamp; 842 } 843 } 844 845 /* Now that the contract terms (and thus the fulfillment URL) are 846 available, subscribe to session-capture triggers if requested. 847 The @e session_eh guard makes this idempotent across the phase 848 re-runs of the long-polling loop; @e gorc->session_eh is cancelled 849 in gorc_cleanup(). */ 850 if ( (NULL != gorc->session_id) && 851 (NULL != gorc->ct->fulfillment_url) && 852 (NULL == gorc->session_eh) ) 853 { 854 struct TMH_SessionEventP session_eh = { 855 .header.size = htons (sizeof (session_eh)), 856 .header.type = htons (TALER_DBEVENT_MERCHANT_SESSION_CAPTURED), 857 .merchant_pub = hc->instance->merchant_pub 858 }; 859 860 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 861 "Subscribing to session triggers for %p\n", 862 gorc); 863 GNUNET_CRYPTO_hash (gorc->session_id, 864 strlen (gorc->session_id), 865 &session_eh.h_session_id); 866 GNUNET_CRYPTO_hash (gorc->ct->fulfillment_url, 867 strlen (gorc->ct->fulfillment_url), 868 &session_eh.h_fulfillment_url); 869 gorc->session_eh 870 = TALER_MERCHANTDB_event_listen ( 871 TMH_db, 872 &session_eh.header, 873 GNUNET_TIME_absolute_get_remaining (gorc->sc.long_poll_timeout), 874 &resume_by_event, 875 gorc); 876 gorc->instant_retry = true; 877 } 878 879 switch (gorc->ct->version) 880 { 881 case TALER_MERCHANT_CONTRACT_VERSION_0: 882 gorc->contract_amount 883 = (NULL != gorc->pc) 884 ? gorc->pc->details.v0.brutto 885 : gorc->order->details.v0.brutto; 886 break; 887 case TALER_MERCHANT_CONTRACT_VERSION_1: 888 if (gorc->choice_index >= 0) 889 { 890 if (gorc->choice_index >= 891 ( (NULL != gorc->pc) 892 ? gorc->pc->details.v1.choices_len 893 : gorc->order->details.v1.choices_len) ) 894 { 895 GNUNET_break (0); 896 phase_end (gorc, 897 TALER_MHD_reply_with_error ( 898 gorc->sc.con, 899 MHD_HTTP_INTERNAL_SERVER_ERROR, 900 TALER_EC_GENERIC_DB_INVARIANT_FAILURE, 901 NULL)); 902 return; 903 } 904 gorc->contract_amount = 905 (NULL != gorc->pc) 906 ? gorc->pc->details.v1.choices[gorc->choice_index].amount 907 : gorc->order->details.v1.choices[gorc->choice_index].amount; 908 } 909 else 910 { 911 GNUNET_break (gorc->order_only); 912 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 913 "Choice index %i is invalid", 914 gorc->choice_index); 915 } 916 break; 917 default: 918 { 919 GNUNET_break (0); 920 phase_end (gorc, 921 TALER_MHD_reply_with_error ( 922 gorc->sc.con, 923 MHD_HTTP_INTERNAL_SERVER_ERROR, 924 TALER_EC_MERCHANT_GET_ORDERS_ID_INVALID_CONTRACT_VERSION, 925 NULL)); 926 return; 927 } 928 } 929 930 if ( (NULL != gorc->contract_terms) && 931 (GNUNET_OK != 932 TALER_JSON_contract_hash (gorc->contract_terms_json, 933 &gorc->h_contract_terms)) ) 934 { 935 GNUNET_break (0); 936 phase_end (gorc, 937 TALER_MHD_reply_with_error (gorc->sc.con, 938 MHD_HTTP_INTERNAL_SERVER_ERROR, 939 TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH, 940 NULL)); 941 return; 942 } 943 GNUNET_assert ( (NULL != gorc->contract_terms_json) || 944 (NULL != gorc->order_json) ); 945 GNUNET_assert ( (NULL != gorc->pc) || 946 (NULL != gorc->order) ); 947 gorc->phase++; 948 } 949 950 951 /** 952 * Check payment status of the order. 953 * 954 * @param[in,out] gorc order context to update 955 */ 956 static void 957 phase_check_paid (struct GetOrderRequestContext *gorc) 958 { 959 struct TMH_HandlerContext *hc = gorc->hc; 960 961 if (gorc->order_only) 962 { 963 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 964 "Order %s unclaimed, no need to lookup payment status\n", 965 hc->infix); 966 GNUNET_assert (! gorc->paid); 967 GNUNET_assert (! gorc->wired); 968 gorc->phase++; 969 return; 970 } 971 if (NULL == gorc->session_id) 972 { 973 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 974 "No session ID, do not need to lookup session-ID specific payment status (%s/%s)\n", 975 gorc->paid ? "paid" : "unpaid", 976 gorc->wired ? "wired" : "unwired"); 977 gorc->phase++; 978 return; 979 } 980 if (! gorc->paid_session_matches) 981 { 982 gorc->paid = false; 983 gorc->wired = false; 984 } 985 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 986 "Order %s %s for session %s (%s)\n", 987 hc->infix, 988 gorc->paid ? "paid" : "unpaid", 989 gorc->session_id, 990 gorc->wired ? "wired" : "unwired"); 991 gorc->phase++; 992 } 993 994 995 /** 996 * Check if the @a reply satisfies the long-poll not_etag 997 * constraint. If so, return it as a response for @a gorc, 998 * otherwise suspend and wait for a change. 999 * 1000 * @param[in,out] gorc request to handle 1001 * @param reply body for JSON response (#MHD_HTTP_OK) 1002 */ 1003 static void 1004 check_reply (struct GetOrderRequestContext *gorc, 1005 const json_t *reply) 1006 { 1007 struct GNUNET_ShortHashCode sh; 1008 unsigned int http_response_code; 1009 bool not_modified; 1010 struct MHD_Response *response; 1011 char *can; 1012 1013 can = TALER_JSON_canonicalize (reply); 1014 GNUNET_assert (GNUNET_YES == 1015 GNUNET_CRYPTO_hkdf_gnunet (&sh, 1016 sizeof (sh), 1017 "GOR-SALT", 1018 strlen ("GOR-SALT"), 1019 can, 1020 strlen (can))); 1021 not_modified = gorc->have_lp_not_etag && 1022 (0 == GNUNET_memcmp (&sh, 1023 &gorc->lp_not_etag)); 1024 1025 if (not_modified && 1026 (! GNUNET_TIME_absolute_is_past (gorc->sc.long_poll_timeout)) ) 1027 { 1028 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1029 "Status unchanged, not returning response yet\n"); 1030 GNUNET_assert (GNUNET_NO == gorc->suspended); 1031 if (gorc->instant_retry) 1032 { 1033 gorc->instant_retry = false; 1034 gorc->phase = GOP_FETCH_CONTRACT; 1035 GNUNET_free (can); 1036 return; 1037 } 1038 /* note: not necessarily actually unpaid ... */ 1039 GNUNET_CONTAINER_DLL_insert (gorc_head, 1040 gorc_tail, 1041 gorc); 1042 gorc->phase = GOP_SUSPENDED_ON_UNPAID; 1043 gorc->suspended = GNUNET_YES; 1044 MHD_suspend_connection (gorc->sc.con); 1045 GNUNET_free (can); 1046 return; 1047 } 1048 { 1049 const char *inm; 1050 1051 inm = MHD_lookup_connection_value (gorc->sc.con, 1052 MHD_HEADER_KIND, 1053 MHD_HTTP_HEADER_IF_NONE_MATCH); 1054 if ( (NULL == inm) || 1055 ('"' != inm[0]) || 1056 ('"' != inm[strlen (inm) - 1]) || 1057 (0 != strncmp (inm + 1, 1058 can, 1059 strlen (can))) ) 1060 not_modified = false; /* must return full response */ 1061 } 1062 GNUNET_free (can); 1063 http_response_code = not_modified 1064 ? MHD_HTTP_NOT_MODIFIED 1065 : MHD_HTTP_OK; 1066 response = TALER_MHD_make_json (reply); 1067 { 1068 char *etag; 1069 char *qetag; 1070 1071 etag = GNUNET_STRINGS_data_to_string_alloc (&sh, 1072 sizeof (sh)); 1073 GNUNET_asprintf (&qetag, 1074 "\"%s\"", 1075 etag); 1076 GNUNET_break (MHD_YES == 1077 MHD_add_response_header (response, 1078 MHD_HTTP_HEADER_ETAG, 1079 qetag)); 1080 GNUNET_free (qetag); 1081 GNUNET_free (etag); 1082 } 1083 1084 { 1085 enum MHD_Result ret; 1086 1087 ret = MHD_queue_response (gorc->sc.con, 1088 http_response_code, 1089 response); 1090 MHD_destroy_response (response); 1091 phase_end (gorc, 1092 ret); 1093 } 1094 } 1095 1096 1097 /** 1098 * Check if re-purchase detection applies to the order. 1099 * 1100 * @param[in,out] gorc order context to update 1101 */ 1102 static void 1103 phase_check_repurchase (struct GetOrderRequestContext *gorc) 1104 { 1105 struct TMH_HandlerContext *hc = gorc->hc; 1106 char *already_paid_order_id = NULL; 1107 enum GNUNET_DB_QueryStatus qs; 1108 char *taler_pay_uri; 1109 char *order_status_url; 1110 json_t *reply; 1111 1112 if ( (gorc->paid) || 1113 (NULL == gorc->ct->fulfillment_url) || 1114 (NULL == gorc->session_id) ) 1115 { 1116 /* Repurchase cannot apply */ 1117 gorc->phase++; 1118 return; 1119 } 1120 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1121 "Running re-purchase detection for %s/%s\n", 1122 gorc->session_id, 1123 gorc->ct->fulfillment_url); 1124 qs = TALER_MERCHANTDB_get_order_by_fulfillment ( 1125 TMH_db, 1126 hc->instance->settings.id, 1127 gorc->ct->fulfillment_url, 1128 gorc->session_id, 1129 TALER_EXCHANGE_YNA_NO != 1130 gorc->allow_refunded_for_repurchase, 1131 &already_paid_order_id); 1132 if (0 > qs) 1133 { 1134 /* single, read-only SQL statements should never cause 1135 serialization problems, and the entry should exist as per above */ 1136 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); 1137 phase_end (gorc, 1138 TALER_MHD_reply_with_error (gorc->sc.con, 1139 MHD_HTTP_INTERNAL_SERVER_ERROR, 1140 TALER_EC_GENERIC_DB_FETCH_FAILED, 1141 "order by fulfillment")); 1142 return; 1143 } 1144 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 1145 { 1146 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1147 "No already paid order for %s/%s\n", 1148 gorc->session_id, 1149 gorc->ct->fulfillment_url); 1150 gorc->phase++; 1151 return; 1152 } 1153 1154 /* User did pay for this order, but under a different session; ask wallet to 1155 switch order ID */ 1156 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1157 "Found already paid order %s\n", 1158 already_paid_order_id); 1159 taler_pay_uri = TMH_make_taler_pay_uri (gorc->sc.con, 1160 hc->infix, 1161 gorc->session_id, 1162 hc->instance->settings.id, 1163 &gorc->claim_token); 1164 order_status_url = TMH_make_order_status_url (gorc->sc.con, 1165 hc->infix, 1166 gorc->session_id, 1167 hc->instance->settings.id, 1168 &gorc->claim_token, 1169 NULL); 1170 if ( (NULL == taler_pay_uri) || 1171 (NULL == order_status_url) ) 1172 { 1173 GNUNET_break_op (0); 1174 GNUNET_free (taler_pay_uri); 1175 GNUNET_free (order_status_url); 1176 phase_end (gorc, 1177 TALER_MHD_reply_with_error (gorc->sc.con, 1178 MHD_HTTP_BAD_REQUEST, 1179 TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED, 1180 "host")); 1181 return; 1182 } 1183 reply = GNUNET_JSON_PACK ( 1184 GNUNET_JSON_pack_string ("taler_pay_uri", 1185 taler_pay_uri), 1186 GNUNET_JSON_pack_string ("order_status_url", 1187 order_status_url), 1188 GNUNET_JSON_pack_string ("order_status", 1189 "unpaid"), 1190 GNUNET_JSON_pack_string ("already_paid_order_id", 1191 already_paid_order_id), 1192 GNUNET_JSON_pack_string ("already_paid_fulfillment_url", 1193 gorc->ct->fulfillment_url), 1194 /* undefined for unpaid v1 contracts */ 1195 GNUNET_JSON_pack_allow_null ( 1196 TALER_JSON_pack_amount ("total_amount", 1197 TALER_amount_is_valid (&gorc->contract_amount) 1198 ? &gorc->contract_amount 1199 : NULL)), 1200 GNUNET_JSON_pack_object_incref ("proto_contract_terms", 1201 gorc->contract_terms_json), 1202 GNUNET_JSON_pack_string ("summary", 1203 gorc->ct->summary), 1204 GNUNET_JSON_pack_timestamp ("pay_deadline", 1205 NULL != gorc->pc 1206 ? gorc->pc->pay_deadline 1207 : gorc->order->pay_deadline), 1208 GNUNET_JSON_pack_timestamp ("creation_time", 1209 gorc->timestamp)); 1210 1211 GNUNET_free (order_status_url); 1212 GNUNET_free (taler_pay_uri); 1213 GNUNET_free (already_paid_order_id); 1214 check_reply (gorc, 1215 reply); 1216 json_decref (reply); 1217 } 1218 1219 1220 /** 1221 * Check if we should suspend until the order is paid. 1222 * 1223 * @param[in,out] gorc order context to update 1224 */ 1225 static void 1226 phase_unpaid_finish (struct GetOrderRequestContext *gorc) 1227 { 1228 struct TMH_HandlerContext *hc = gorc->hc; 1229 char *order_status_url; 1230 1231 if (gorc->paid) 1232 { 1233 gorc->phase++; 1234 return; 1235 } 1236 /* User never paid for this order, suspend waiting 1237 on payment or return details. */ 1238 if (GNUNET_TIME_absolute_is_future (gorc->sc.long_poll_timeout) && 1239 (! gorc->have_lp_not_etag) ) 1240 { 1241 if (gorc->instant_retry) 1242 { 1243 gorc->instant_retry = false; 1244 gorc->phase = GOP_FETCH_CONTRACT; 1245 return; 1246 } 1247 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1248 "Suspending GET /private/orders/%s\n", 1249 hc->infix); 1250 GNUNET_CONTAINER_DLL_insert (gorc_head, 1251 gorc_tail, 1252 gorc); 1253 gorc->phase = GOP_SUSPENDED_ON_UNPAID; 1254 gorc->suspended = GNUNET_YES; 1255 MHD_suspend_connection (gorc->sc.con); 1256 return; 1257 } 1258 order_status_url = TMH_make_order_status_url (gorc->sc.con, 1259 hc->infix, 1260 gorc->session_id, 1261 hc->instance->settings.id, 1262 &gorc->claim_token, 1263 NULL); 1264 if (! gorc->order_only) 1265 { 1266 json_t *reply; 1267 1268 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1269 "Order %s claimed but not paid yet\n", 1270 hc->infix); 1271 reply = GNUNET_JSON_PACK ( 1272 GNUNET_JSON_pack_string ("order_status_url", 1273 order_status_url), 1274 GNUNET_JSON_pack_object_incref ("contract_terms", 1275 gorc->contract_terms_json), 1276 GNUNET_JSON_pack_string ("order_status", 1277 "claimed")); 1278 GNUNET_free (order_status_url); 1279 check_reply (gorc, 1280 reply); 1281 json_decref (reply); 1282 return; 1283 } 1284 { 1285 char *taler_pay_uri; 1286 json_t *reply; 1287 1288 taler_pay_uri = TMH_make_taler_pay_uri (gorc->sc.con, 1289 hc->infix, 1290 gorc->session_id, 1291 hc->instance->settings.id, 1292 &gorc->claim_token); 1293 reply = GNUNET_JSON_PACK ( 1294 GNUNET_JSON_pack_string ("taler_pay_uri", 1295 taler_pay_uri), 1296 GNUNET_JSON_pack_string ("order_status_url", 1297 order_status_url), 1298 GNUNET_JSON_pack_string ("order_status", 1299 "unpaid"), 1300 GNUNET_JSON_pack_object_incref ("proto_contract_terms", 1301 gorc->contract_terms_json), 1302 /* undefined for unpaid v1 contracts */ 1303 GNUNET_JSON_pack_allow_null ( 1304 TALER_JSON_pack_amount ("total_amount", 1305 &gorc->contract_amount)), 1306 GNUNET_JSON_pack_string ("summary", 1307 gorc->ct->summary), 1308 GNUNET_JSON_pack_timestamp ("creation_time", 1309 gorc->timestamp)); 1310 check_reply (gorc, 1311 reply); 1312 json_decref (reply); 1313 GNUNET_free (taler_pay_uri); 1314 } 1315 GNUNET_free (order_status_url); 1316 } 1317 1318 1319 /** 1320 * Function called with each @a coin_pub that was deposited into the 1321 * @a h_wire account of the merchant for the @a deposit_serial as part 1322 * of the payment for the order identified by @a cls. 1323 * 1324 * Queries the exchange for the payment status associated with the 1325 * given coin. 1326 * 1327 * @param cls a `struct GetOrderRequestContext` 1328 * @param deposit_serial identifies the deposit operation 1329 * @param exchange_url URL of the exchange that issued @a coin_pub 1330 * @param h_wire hash of the merchant's wire account into which the deposit was made 1331 * @param deposit_timestamp when was the deposit made 1332 * @param amount_with_fee amount the exchange will deposit for this coin 1333 * @param deposit_fee fee the exchange will charge for this coin 1334 * @param coin_pub public key of the deposited coin 1335 */ 1336 static void 1337 deposit_cb ( 1338 void *cls, 1339 uint64_t deposit_serial, 1340 const char *exchange_url, 1341 const struct TALER_MerchantWireHashP *h_wire, 1342 struct GNUNET_TIME_Timestamp deposit_timestamp, 1343 const struct TALER_Amount *amount_with_fee, 1344 const struct TALER_Amount *deposit_fee, 1345 const struct TALER_CoinSpendPublicKeyP *coin_pub) 1346 { 1347 struct GetOrderRequestContext *gorc = cls; 1348 struct TransferQuery *tq; 1349 1350 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1351 "Checking deposit status for coin %s (over %s)\n", 1352 TALER_B2S (coin_pub), 1353 TALER_amount2s (amount_with_fee)); 1354 gorc->last_payment 1355 = GNUNET_TIME_timestamp_max (gorc->last_payment, 1356 deposit_timestamp); 1357 tq = GNUNET_new (struct TransferQuery); 1358 tq->gorc = gorc; 1359 tq->exchange_url = GNUNET_strdup (exchange_url); 1360 tq->deposit_serial = deposit_serial; 1361 GNUNET_CONTAINER_DLL_insert (gorc->tq_head, 1362 gorc->tq_tail, 1363 tq); 1364 tq->coin_pub = *coin_pub; 1365 tq->h_wire = *h_wire; 1366 tq->amount_with_fee = *amount_with_fee; 1367 tq->deposit_fee = *deposit_fee; 1368 } 1369 1370 1371 /** 1372 * Check wire transfer status for the order at the exchange. 1373 * 1374 * @param[in,out] gorc order context to update 1375 */ 1376 static void 1377 phase_check_deposits (struct GetOrderRequestContext *gorc) 1378 { 1379 GNUNET_assert (! gorc->order_only); 1380 GNUNET_assert (gorc->paid); 1381 1382 /* amount must be always valid for paid orders */ 1383 GNUNET_assert (GNUNET_OK == 1384 TALER_amount_is_valid (&gorc->contract_amount)); 1385 1386 GNUNET_assert (GNUNET_OK == 1387 TALER_amount_set_zero (gorc->contract_amount.currency, 1388 &gorc->deposits_total)); 1389 GNUNET_assert (GNUNET_OK == 1390 TALER_amount_set_zero (gorc->contract_amount.currency, 1391 &gorc->deposit_fees_total)); 1392 GNUNET_assert (GNUNET_OK == 1393 TALER_amount_set_zero (gorc->contract_amount.currency, 1394 &gorc->deposit_fees_refunded_total)); 1395 TALER_MERCHANTDB_iterate_deposits_by_order (TMH_db, 1396 gorc->order_serial, 1397 &deposit_cb, 1398 gorc); 1399 gorc->phase++; 1400 } 1401 1402 1403 /** 1404 * Function called with information about a refund. 1405 * It is responsible for summing up the refund amount. 1406 * 1407 * @param cls closure 1408 * @param refund_serial unique serial number of the refund 1409 * @param timestamp time of the refund (for grouping of refunds in the wallet UI) 1410 * @param coin_pub public coin from which the refund comes from 1411 * @param exchange_url URL of the exchange that issued @a coin_pub 1412 * @param rtransaction_id identificator of the refund 1413 * @param reason human-readable explanation of the refund 1414 * @param refund_amount refund amount which is being taken from @a coin_pub 1415 * @param pending true if the this refund was not yet processed by the wallet/exchange 1416 */ 1417 static void 1418 process_refunds_cb ( 1419 void *cls, 1420 uint64_t refund_serial, 1421 struct GNUNET_TIME_Timestamp timestamp, 1422 const struct TALER_CoinSpendPublicKeyP *coin_pub, 1423 const char *exchange_url, 1424 uint64_t rtransaction_id, 1425 const char *reason, 1426 const struct TALER_Amount *refund_amount, 1427 bool pending) 1428 { 1429 struct GetOrderRequestContext *gorc = cls; 1430 1431 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1432 "Found refund %llu over %s for reason %s\n", 1433 (unsigned long long) rtransaction_id, 1434 TALER_amount2s (refund_amount), 1435 reason); 1436 GNUNET_assert ( 1437 0 == 1438 json_array_append_new ( 1439 gorc->refund_details, 1440 GNUNET_JSON_PACK ( 1441 TALER_JSON_pack_amount ("amount", 1442 refund_amount), 1443 GNUNET_JSON_pack_bool ("pending", 1444 pending), 1445 GNUNET_JSON_pack_timestamp ("timestamp", 1446 timestamp), 1447 GNUNET_JSON_pack_string ("reason", 1448 reason)))); 1449 /* For refunded coins, we are not charged deposit fees, so subtract those 1450 again */ 1451 for (struct TransferQuery *tq = gorc->tq_head; 1452 NULL != tq; 1453 tq = tq->next) 1454 { 1455 if (0 != 1456 strcmp (exchange_url, 1457 tq->exchange_url)) 1458 continue; 1459 if (0 != 1460 GNUNET_memcmp (&tq->coin_pub, 1461 coin_pub)) 1462 continue; 1463 if (GNUNET_OK != 1464 TALER_amount_cmp_currency ( 1465 &gorc->deposit_fees_total, 1466 &tq->deposit_fee)) 1467 { 1468 gorc->refund_currency_mismatch = true; 1469 return; 1470 } 1471 GNUNET_assert ( 1472 0 <= 1473 TALER_amount_add (&gorc->deposit_fees_refunded_total, 1474 &gorc->deposit_fees_refunded_total, 1475 &tq->deposit_fee)); 1476 } 1477 if (GNUNET_OK != 1478 TALER_amount_cmp_currency ( 1479 &gorc->refund_amount, 1480 refund_amount)) 1481 { 1482 gorc->refund_currency_mismatch = true; 1483 return; 1484 } 1485 GNUNET_assert (0 <= 1486 TALER_amount_add (&gorc->refund_amount, 1487 &gorc->refund_amount, 1488 refund_amount)); 1489 gorc->refunded = true; 1490 gorc->refund_pending |= pending; 1491 } 1492 1493 1494 /** 1495 * Check refund status for the order. 1496 * 1497 * @param[in,out] gorc order context to update 1498 */ 1499 static void 1500 phase_check_refunds (struct GetOrderRequestContext *gorc) 1501 { 1502 struct TMH_HandlerContext *hc = gorc->hc; 1503 enum GNUNET_DB_QueryStatus qs; 1504 1505 GNUNET_assert (! gorc->order_only); 1506 GNUNET_assert (gorc->paid); 1507 1508 /* Accumulate refunds, if any. */ 1509 GNUNET_assert (GNUNET_OK == 1510 TALER_amount_set_zero (gorc->contract_amount.currency, 1511 &gorc->refund_amount)); 1512 json_array_clear (gorc->refund_details); 1513 qs = TALER_MERCHANTDB_iterate_refunds_detailed ( 1514 TMH_db, 1515 hc->instance->settings.id, 1516 &gorc->h_contract_terms, 1517 &process_refunds_cb, 1518 gorc); 1519 if (0 > qs) 1520 { 1521 GNUNET_break (0); 1522 phase_end (gorc, 1523 TALER_MHD_reply_with_error ( 1524 gorc->sc.con, 1525 MHD_HTTP_INTERNAL_SERVER_ERROR, 1526 TALER_EC_GENERIC_DB_FETCH_FAILED, 1527 "detailed refunds")); 1528 return; 1529 } 1530 if (gorc->refund_currency_mismatch) 1531 { 1532 GNUNET_break (0); 1533 phase_end (gorc, 1534 TALER_MHD_reply_with_error ( 1535 gorc->sc.con, 1536 MHD_HTTP_INTERNAL_SERVER_ERROR, 1537 TALER_EC_GENERIC_DB_FETCH_FAILED, 1538 "refunds in different currency than original order price")); 1539 return; 1540 } 1541 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1542 "Total refunds are %s\n", 1543 TALER_amount2s (&gorc->refund_amount)); 1544 gorc->phase++; 1545 } 1546 1547 1548 /** 1549 * Function called with available wire details, to be added to 1550 * the response. 1551 * 1552 * @param cls a `struct GetOrderRequestContext` 1553 * @param wtid wire transfer subject of the wire transfer for the coin 1554 * @param exchange_url base URL of the exchange that made the payment 1555 * @param execution_time when was the payment made 1556 * @param deposit_value contribution of the coin to the total wire transfer value 1557 * @param deposit_fee deposit fee charged by the exchange for the coin 1558 * @param transfer_confirmed did the merchant confirm that a wire transfer with 1559 * @a wtid over the total amount happened? 1560 * @param expected_credit_serial row for the expected wire transfer this 1561 * entry references 1562 */ 1563 static void 1564 process_transfer_details ( 1565 void *cls, 1566 const struct TALER_WireTransferIdentifierRawP *wtid, 1567 const char *exchange_url, 1568 struct GNUNET_TIME_Timestamp execution_time, 1569 const struct TALER_Amount *deposit_value, 1570 const struct TALER_Amount *deposit_fee, 1571 bool transfer_confirmed, 1572 uint64_t expected_credit_serial) 1573 { 1574 struct GetOrderRequestContext *gorc = cls; 1575 json_t *wire_details = gorc->wire_details; 1576 struct TALER_Amount wired; 1577 1578 if ( (GNUNET_OK != 1579 TALER_amount_cmp_currency (&gorc->deposits_total, 1580 deposit_value)) || 1581 (GNUNET_OK != 1582 TALER_amount_cmp_currency (&gorc->deposit_fees_total, 1583 deposit_fee)) ) 1584 { 1585 GNUNET_break (0); 1586 gorc->deposit_currency_mismatch = true; 1587 return; 1588 } 1589 1590 /* Compute total amount *wired* */ 1591 GNUNET_assert (0 <= 1592 TALER_amount_add (&gorc->deposits_total, 1593 &gorc->deposits_total, 1594 deposit_value)); 1595 GNUNET_assert (0 <= 1596 TALER_amount_add (&gorc->deposit_fees_total, 1597 &gorc->deposit_fees_total, 1598 deposit_fee)); 1599 GNUNET_assert (0 <= TALER_amount_subtract (&wired, 1600 deposit_value, 1601 deposit_fee)); 1602 GNUNET_assert (0 == 1603 json_array_append_new ( 1604 wire_details, 1605 GNUNET_JSON_PACK ( 1606 GNUNET_JSON_pack_data_auto ("wtid", 1607 wtid), 1608 GNUNET_JSON_pack_string ("exchange_url", 1609 exchange_url), 1610 TALER_JSON_pack_amount ("amount", 1611 &wired), 1612 TALER_JSON_pack_amount ("deposit_fee", 1613 deposit_fee), 1614 GNUNET_JSON_pack_timestamp ("execution_time", 1615 execution_time), 1616 GNUNET_JSON_pack_bool ("confirmed", 1617 transfer_confirmed), 1618 GNUNET_JSON_pack_uint64 ("expected_transfer_serial_id", 1619 expected_credit_serial)))); 1620 } 1621 1622 1623 /** 1624 * Check transfer status in local database. 1625 * 1626 * @param[in,out] gorc order context to update 1627 */ 1628 static void 1629 phase_check_local_transfers (struct GetOrderRequestContext *gorc) 1630 { 1631 struct TMH_HandlerContext *hc = gorc->hc; 1632 enum GNUNET_DB_QueryStatus qs; 1633 1634 GNUNET_assert (gorc->paid); 1635 GNUNET_assert (! gorc->order_only); 1636 1637 GNUNET_assert (GNUNET_OK == 1638 TALER_amount_set_zero (gorc->contract_amount.currency, 1639 &gorc->deposits_total)); 1640 GNUNET_assert (GNUNET_OK == 1641 TALER_amount_set_zero (gorc->contract_amount.currency, 1642 &gorc->deposit_fees_total)); 1643 GNUNET_assert (NULL != gorc->wire_details); 1644 /* We may be running again due to long-polling, clear state first */ 1645 json_array_clear (gorc->wire_details); 1646 qs = TALER_MERCHANTDB_iterate_transfer_details_by_order (TMH_db, 1647 gorc->order_serial, 1648 &process_transfer_details, 1649 gorc); 1650 if (0 > qs) 1651 { 1652 GNUNET_break (0); 1653 phase_end (gorc, 1654 TALER_MHD_reply_with_error (gorc->sc.con, 1655 MHD_HTTP_INTERNAL_SERVER_ERROR, 1656 TALER_EC_GENERIC_DB_FETCH_FAILED, 1657 "transfer details")); 1658 return; 1659 } 1660 if (gorc->deposit_currency_mismatch) 1661 { 1662 GNUNET_break (0); 1663 phase_end (gorc, 1664 TALER_MHD_reply_with_error (gorc->sc.con, 1665 MHD_HTTP_INTERNAL_SERVER_ERROR, 1666 TALER_EC_GENERIC_DB_FETCH_FAILED, 1667 "deposits in different currency than original order price")); 1668 return; 1669 } 1670 1671 if (! gorc->wired) 1672 { 1673 /* we believe(d) the wire transfer did not happen yet, check if maybe 1674 in light of new evidence it did */ 1675 struct TALER_Amount expect_total; 1676 1677 if (0 > 1678 TALER_amount_subtract (&expect_total, 1679 &gorc->contract_amount, 1680 &gorc->refund_amount)) 1681 { 1682 GNUNET_break (0); 1683 phase_end (gorc, 1684 TALER_MHD_reply_with_error ( 1685 gorc->sc.con, 1686 MHD_HTTP_INTERNAL_SERVER_ERROR, 1687 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 1688 "refund exceeds contract value")); 1689 return; 1690 } 1691 GNUNET_assert ( 1692 0 <= 1693 TALER_amount_add (&expect_total, 1694 &expect_total, 1695 &gorc->deposit_fees_refunded_total)); 1696 1697 if (0 > 1698 TALER_amount_subtract (&expect_total, 1699 &expect_total, 1700 &gorc->deposit_fees_total)) 1701 { 1702 GNUNET_break (0); 1703 phase_end (gorc, 1704 TALER_MHD_reply_with_error ( 1705 gorc->sc.con, 1706 MHD_HTTP_INTERNAL_SERVER_ERROR, 1707 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 1708 "deposit fees exceed total minus refunds")); 1709 return; 1710 } 1711 if (0 >= 1712 TALER_amount_cmp (&expect_total, 1713 &gorc->deposits_total)) 1714 { 1715 /* expect_total <= gorc->deposits_total: good: we got the wire transfer */ 1716 gorc->wired = true; 1717 qs = TALER_MERCHANTDB_update_to_contract_terms_wired (TMH_db, 1718 gorc->order_serial); 1719 GNUNET_break (qs >= 0); /* just warn if transaction failed */ 1720 TMH_notify_order_change (hc->instance, 1721 TMH_OSF_PAID 1722 | TMH_OSF_WIRED, 1723 gorc->timestamp, 1724 gorc->order_serial); 1725 } 1726 } 1727 gorc->phase++; 1728 } 1729 1730 1731 /** 1732 * Generate final result for the status request. 1733 * 1734 * @param[in,out] gorc order context to update 1735 */ 1736 static void 1737 phase_reply_result (struct GetOrderRequestContext *gorc) 1738 { 1739 struct TMH_HandlerContext *hc = gorc->hc; 1740 char *order_status_url; 1741 1742 GNUNET_assert (gorc->paid); 1743 GNUNET_assert (! gorc->order_only); 1744 1745 { 1746 struct TALER_PrivateContractHashP *h_contract = NULL; 1747 1748 /* In a session-bound payment, allow the browser to check the order 1749 * status page (e.g. to get a refund). 1750 * 1751 * Note that we don't allow this outside of session-based payment, as 1752 * otherwise this becomes an oracle to convert order_id to h_contract. 1753 */ 1754 if (NULL != gorc->session_id) 1755 h_contract = &gorc->h_contract_terms; 1756 1757 order_status_url = 1758 TMH_make_order_status_url (gorc->sc.con, 1759 hc->infix, 1760 gorc->session_id, 1761 hc->instance->settings.id, 1762 &gorc->claim_token, 1763 h_contract); 1764 } 1765 if (GNUNET_TIME_absolute_is_zero (gorc->last_payment.abs_time)) 1766 { 1767 GNUNET_break (GNUNET_YES == 1768 TALER_amount_is_zero (&gorc->contract_amount)); 1769 gorc->last_payment = gorc->timestamp; 1770 } 1771 { 1772 json_t *reply; 1773 1774 reply = GNUNET_JSON_PACK ( 1775 // Deprecated in protocol v6! 1776 GNUNET_JSON_pack_array_steal ("wire_reports", 1777 json_array ()), 1778 GNUNET_JSON_pack_uint64 ("exchange_code", 1779 gorc->exchange_ec), 1780 GNUNET_JSON_pack_uint64 ("exchange_http_status", 1781 gorc->exchange_hc), 1782 /* legacy: */ 1783 GNUNET_JSON_pack_uint64 ("exchange_ec", 1784 gorc->exchange_ec), 1785 /* legacy: */ 1786 GNUNET_JSON_pack_uint64 ("exchange_hc", 1787 gorc->exchange_hc), 1788 TALER_JSON_pack_amount ("deposit_total", 1789 &gorc->deposits_total), 1790 GNUNET_JSON_pack_object_incref ("contract_terms", 1791 gorc->contract_terms_json), 1792 GNUNET_JSON_pack_string ("order_status", 1793 "paid"), 1794 GNUNET_JSON_pack_timestamp ("last_payment", 1795 gorc->last_payment), 1796 GNUNET_JSON_pack_bool ("refunded", 1797 gorc->refunded), 1798 GNUNET_JSON_pack_bool ("wired", 1799 gorc->wired), 1800 GNUNET_JSON_pack_bool ("refund_pending", 1801 gorc->refund_pending), 1802 GNUNET_JSON_pack_allow_null ( 1803 TALER_JSON_pack_amount ("refund_amount", 1804 &gorc->refund_amount)), 1805 GNUNET_JSON_pack_array_incref ("wire_details", 1806 gorc->wire_details), 1807 GNUNET_JSON_pack_array_incref ("refund_details", 1808 gorc->refund_details), 1809 GNUNET_JSON_pack_string ("order_status_url", 1810 order_status_url), 1811 (gorc->choice_index >= 0) 1812 ? GNUNET_JSON_pack_int64 ("choice_index", 1813 gorc->choice_index) 1814 : GNUNET_JSON_pack_end_ ()); 1815 check_reply (gorc, 1816 reply); 1817 json_decref (reply); 1818 } 1819 GNUNET_free (order_status_url); 1820 } 1821 1822 1823 /** 1824 * End with error status in wire_hc and wire_ec. 1825 * 1826 * @param[in,out] gorc order context to update 1827 */ 1828 static void 1829 phase_error (struct GetOrderRequestContext *gorc) 1830 { 1831 GNUNET_assert (TALER_EC_NONE != gorc->wire_ec); 1832 phase_end (gorc, 1833 TALER_MHD_reply_with_error (gorc->sc.con, 1834 gorc->wire_hc, 1835 gorc->wire_ec, 1836 NULL)); 1837 } 1838 1839 1840 enum MHD_Result 1841 TMH_private_get_orders_ID ( 1842 const struct TMH_RequestHandler *rh, 1843 struct MHD_Connection *connection, 1844 struct TMH_HandlerContext *hc) 1845 { 1846 struct GetOrderRequestContext *gorc = hc->ctx; 1847 1848 if (NULL == gorc) 1849 { 1850 /* First time here, parse request and check order is known */ 1851 GNUNET_assert (NULL != hc->infix); 1852 gorc = GNUNET_new (struct GetOrderRequestContext); 1853 hc->cc = &gorc_cleanup; 1854 hc->ctx = gorc; 1855 gorc->sc.con = connection; 1856 gorc->hc = hc; 1857 gorc->wire_details = json_array (); 1858 GNUNET_assert (NULL != gorc->wire_details); 1859 gorc->refund_details = json_array (); 1860 GNUNET_assert (NULL != gorc->refund_details); 1861 gorc->session_id = MHD_lookup_connection_value (connection, 1862 MHD_GET_ARGUMENT_KIND, 1863 "session_id"); 1864 if (! (TALER_MHD_arg_to_yna (connection, 1865 "allow_refunded_for_repurchase", 1866 TALER_EXCHANGE_YNA_NO, 1867 &gorc->allow_refunded_for_repurchase)) ) 1868 return TALER_MHD_reply_with_error (connection, 1869 MHD_HTTP_BAD_REQUEST, 1870 TALER_EC_GENERIC_PARAMETER_MALFORMED, 1871 "allow_refunded_for_repurchase"); 1872 TALER_MHD_parse_request_timeout (connection, 1873 &gorc->sc.long_poll_timeout); 1874 TALER_MHD_parse_request_arg_auto (connection, 1875 "lp_not_etag", 1876 &gorc->lp_not_etag, 1877 gorc->have_lp_not_etag); 1878 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1879 "Starting GET /private/orders/%s processing with timeout %s\n", 1880 hc->infix, 1881 GNUNET_STRINGS_absolute_time_to_string ( 1882 gorc->sc.long_poll_timeout)); 1883 } 1884 if (GNUNET_SYSERR == gorc->suspended) 1885 return MHD_NO; /* we are in shutdown */ 1886 while (1) 1887 { 1888 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1889 "Processing order %s in phase %d\n", 1890 hc->infix, 1891 (int) gorc->phase); 1892 switch (gorc->phase) 1893 { 1894 case GOP_INIT: 1895 phase_init (gorc); 1896 break; 1897 case GOP_FETCH_CONTRACT: 1898 phase_fetch_contract (gorc); 1899 break; 1900 case GOP_PARSE_CONTRACT: 1901 phase_parse_contract (gorc); 1902 break; 1903 case GOP_CHECK_PAID: 1904 phase_check_paid (gorc); 1905 break; 1906 case GOP_CHECK_REPURCHASE: 1907 phase_check_repurchase (gorc); 1908 break; 1909 case GOP_UNPAID_FINISH: 1910 phase_unpaid_finish (gorc); 1911 break; 1912 case GOP_CHECK_DEPOSITS: 1913 phase_check_deposits (gorc); 1914 break; 1915 case GOP_CHECK_REFUNDS: 1916 phase_check_refunds (gorc); 1917 break; 1918 case GOP_CHECK_LOCAL_TRANSFERS: 1919 phase_check_local_transfers (gorc); 1920 break; 1921 case GOP_REPLY_RESULT: 1922 phase_reply_result (gorc); 1923 break; 1924 case GOP_ERROR: 1925 phase_error (gorc); 1926 break; 1927 case GOP_SUSPENDED_ON_UNPAID: 1928 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1929 "Suspending order request awaiting payment\n"); 1930 return MHD_YES; 1931 case GOP_END_YES: 1932 return MHD_YES; 1933 case GOP_END_NO: 1934 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1935 "Closing connection, no response generated\n"); 1936 return MHD_NO; 1937 } 1938 } /* end first-time per-request initialization */ 1939 }