taler-merchant-httpd_get-private-orders-ORDER_ID.c (59062B)
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/lookup_contract_terms3.h" 36 #include "merchant-database/lookup_deposits_by_order.h" 37 #include "merchant-database/lookup_order.h" 38 #include "merchant-database/lookup_order_by_fulfillment.h" 39 #include "merchant-database/lookup_refunds_detailed.h" 40 #include "merchant-database/lookup_transfer_details_by_order.h" 41 #include "merchant-database/mark_order_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 * Check if the (paid) order was refunded. 147 */ 148 GOP_CHECK_REFUNDS = 6, 149 150 /** 151 * Load all deposits associated with the order. 152 */ 153 GOP_CHECK_DEPOSITS = 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 * True if the exchange wired the money to the merchant. 434 */ 435 bool wired; 436 437 /** 438 * True if the order remains unclaimed. 439 */ 440 bool order_only; 441 442 /** 443 * Set to true if this payment has been refunded and 444 * some refunds remain to be picked up by the wallet. 445 */ 446 bool refund_pending; 447 448 /** 449 * Set to true if our database (incorrectly) has refunds 450 * in a different currency than the currency of the 451 * original payment for the order. 452 */ 453 bool refund_currency_mismatch; 454 455 /** 456 * Set to true if our database (incorrectly) has deposits 457 * in a different currency than the currency of the 458 * original payment for the order. 459 */ 460 bool deposit_currency_mismatch; 461 }; 462 463 464 /** 465 * Head of list of suspended requests waiting on the exchange. 466 */ 467 static struct GetOrderRequestContext *gorc_head; 468 469 /** 470 * Tail of list of suspended requests waiting on the exchange. 471 */ 472 static struct GetOrderRequestContext *gorc_tail; 473 474 475 void 476 TMH_force_gorc_resume (void) 477 { 478 struct GetOrderRequestContext *gorc; 479 480 while (NULL != (gorc = gorc_head)) 481 { 482 GNUNET_CONTAINER_DLL_remove (gorc_head, 483 gorc_tail, 484 gorc); 485 GNUNET_assert (GNUNET_YES == gorc->suspended); 486 gorc->suspended = GNUNET_SYSERR; 487 MHD_resume_connection (gorc->sc.con); 488 } 489 } 490 491 492 /** 493 * We have received a trigger from the database 494 * that we should (possibly) resume the request. 495 * 496 * @param cls a `struct GetOrderRequestContext` to resume 497 * @param extra string encoding refund amount (or NULL) 498 * @param extra_size number of bytes in @a extra 499 */ 500 static void 501 resume_by_event (void *cls, 502 const void *extra, 503 size_t extra_size) 504 { 505 struct GetOrderRequestContext *gorc = cls; 506 507 (void) extra; 508 (void) extra_size; 509 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 510 "Resuming request for order %s by trigger\n", 511 gorc->hc->infix); 512 if (GNUNET_NO == gorc->suspended) 513 return; /* duplicate event is possible */ 514 gorc->suspended = GNUNET_NO; 515 gorc->phase = GOP_FETCH_CONTRACT; 516 GNUNET_CONTAINER_DLL_remove (gorc_head, 517 gorc_tail, 518 gorc); 519 MHD_resume_connection (gorc->sc.con); 520 TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */ 521 } 522 523 524 /** 525 * Clean up the session state for a GET /private/order/ID request. 526 * 527 * @param cls closure, must be a `struct GetOrderRequestContext *` 528 */ 529 static void 530 gorc_cleanup (void *cls) 531 { 532 struct GetOrderRequestContext *gorc = cls; 533 struct TransferQuery *tq; 534 535 while (NULL != (tq = gorc->tq_head)) 536 { 537 GNUNET_CONTAINER_DLL_remove (gorc->tq_head, 538 gorc->tq_tail, 539 tq); 540 GNUNET_free (tq->exchange_url); 541 GNUNET_free (tq); 542 } 543 544 if (NULL != gorc->contract_terms_json) 545 { 546 json_decref (gorc->contract_terms_json); 547 gorc->contract_terms_json = NULL; 548 } 549 if (NULL != gorc->order_json) 550 { 551 json_decref (gorc->order_json); 552 gorc->order_json = NULL; 553 } 554 if (NULL != gorc->contract_terms) 555 { 556 TALER_MERCHANT_contract_free (gorc->contract_terms); 557 gorc->contract_terms = NULL; 558 gorc->pc = NULL; /* was an alias! */ 559 } 560 if (NULL != gorc->pc) 561 { 562 TALER_MERCHANT_proto_contract_free (gorc->pc); 563 gorc->pc = NULL; 564 } 565 if (NULL != gorc->order) 566 { 567 TALER_MERCHANT_order_free (gorc->order); 568 gorc->order = NULL; 569 } 570 gorc->ct = NULL; /* avoid dangling pointer */ 571 if (NULL != gorc->wire_details) 572 json_decref (gorc->wire_details); 573 if (NULL != gorc->refund_details) 574 json_decref (gorc->refund_details); 575 if (NULL != gorc->tt) 576 { 577 GNUNET_SCHEDULER_cancel (gorc->tt); 578 gorc->tt = NULL; 579 } 580 if (NULL != gorc->eh) 581 { 582 TALER_MERCHANTDB_event_listen_cancel (gorc->eh); 583 gorc->eh = NULL; 584 } 585 if (NULL != gorc->session_eh) 586 { 587 TALER_MERCHANTDB_event_listen_cancel (gorc->session_eh); 588 gorc->session_eh = NULL; 589 } 590 GNUNET_free (gorc); 591 } 592 593 594 /** 595 * Processing the request @a gorc is finished, set the 596 * final return value in phase based on @a mret. 597 * 598 * @param[in,out] gorc order context to initialize 599 * @param mret MHD HTTP response status to return 600 */ 601 static void 602 phase_end (struct GetOrderRequestContext *gorc, 603 enum MHD_Result mret) 604 { 605 gorc->phase = (MHD_YES == mret) 606 ? GOP_END_YES 607 : GOP_END_NO; 608 } 609 610 611 /** 612 * Initialize event callbacks for the order processing. 613 * 614 * @param[in,out] gorc order context to initialize 615 */ 616 static void 617 phase_init (struct GetOrderRequestContext *gorc) 618 { 619 struct TMH_HandlerContext *hc = gorc->hc; 620 struct TMH_OrderPayEventP pay_eh = { 621 .header.size = htons (sizeof (pay_eh)), 622 .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_STATUS_CHANGED), 623 .merchant_pub = hc->instance->merchant_pub 624 }; 625 626 if (! GNUNET_TIME_absolute_is_future (gorc->sc.long_poll_timeout)) 627 { 628 gorc->phase++; 629 return; 630 } 631 632 GNUNET_CRYPTO_hash (hc->infix, 633 strlen (hc->infix), 634 &pay_eh.h_order_id); 635 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 636 "Subscribing to payment triggers for %p\n", 637 gorc); 638 gorc->eh = TALER_MERCHANTDB_event_listen ( 639 TMH_db, 640 &pay_eh.header, 641 GNUNET_TIME_absolute_get_remaining (gorc->sc.long_poll_timeout), 642 &resume_by_event, 643 gorc); 644 if ( (NULL != gorc->session_id) && 645 (NULL != gorc->ct->fulfillment_url) ) 646 { 647 struct TMH_SessionEventP session_eh = { 648 .header.size = htons (sizeof (session_eh)), 649 .header.type = htons (TALER_DBEVENT_MERCHANT_SESSION_CAPTURED), 650 .merchant_pub = hc->instance->merchant_pub 651 }; 652 653 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 654 "Subscribing to session triggers for %p\n", 655 gorc); 656 GNUNET_CRYPTO_hash (gorc->session_id, 657 strlen (gorc->session_id), 658 &session_eh.h_session_id); 659 GNUNET_CRYPTO_hash (gorc->ct->fulfillment_url, 660 strlen (gorc->ct->fulfillment_url), 661 &session_eh.h_fulfillment_url); 662 gorc->session_eh 663 = TALER_MERCHANTDB_event_listen ( 664 TMH_db, 665 &session_eh.header, 666 GNUNET_TIME_absolute_get_remaining (gorc->sc.long_poll_timeout), 667 &resume_by_event, 668 gorc); 669 } 670 gorc->phase++; 671 } 672 673 674 /** 675 * Obtain latest contract terms from the database. 676 * 677 * @param[in,out] gorc order context to update 678 */ 679 static void 680 phase_fetch_contract (struct GetOrderRequestContext *gorc) 681 { 682 struct TMH_HandlerContext *hc = gorc->hc; 683 enum GNUNET_DB_QueryStatus qs; 684 685 if (NULL != gorc->contract_terms_json) 686 { 687 /* Free memory filled with old contract terms before fetching the latest 688 ones from the DB. Note that we cannot simply skip the database 689 interaction as the contract terms loaded previously might be from an 690 earlier *unclaimed* order state (which we loaded in a previous 691 invocation of this function and we are back here due to long polling) 692 and thus the contract terms could have changed during claiming. Thus, 693 we need to fetch the latest contract terms from the DB again. */ 694 json_decref (gorc->contract_terms_json); 695 gorc->contract_terms_json = NULL; 696 gorc->order_only = false; 697 } 698 TALER_MERCHANTDB_preflight (TMH_db); 699 qs = TALER_MERCHANTDB_lookup_contract_terms3 (TMH_db, 700 hc->instance->settings.id, 701 hc->infix, 702 gorc->session_id, 703 &gorc->contract_terms_json, 704 &gorc->order_serial, 705 &gorc->paid, 706 &gorc->wired, 707 &gorc->paid_session_matches, 708 &gorc->claim_token, 709 &gorc->choice_index); 710 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 711 "lookup_contract_terms (%s) returned %d\n", 712 hc->infix, 713 (int) qs); 714 if (0 > qs) 715 { 716 /* single, read-only SQL statements should never cause 717 serialization problems */ 718 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); 719 /* Always report on hard error as well to enable diagnostics */ 720 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); 721 phase_end (gorc, 722 TALER_MHD_reply_with_error (gorc->sc.con, 723 MHD_HTTP_INTERNAL_SERVER_ERROR, 724 TALER_EC_GENERIC_DB_FETCH_FAILED, 725 "contract terms")); 726 return; 727 } 728 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) 729 { 730 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 731 "Order %s is %s (%s) according to database, choice %d\n", 732 hc->infix, 733 gorc->paid ? "paid" : "unpaid", 734 gorc->wired ? "wired" : "unwired", 735 (int) gorc->choice_index); 736 gorc->phase++; 737 return; 738 } 739 GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs); 740 GNUNET_assert (! gorc->paid); 741 /* No contract, only order, fetch from orders table */ 742 gorc->order_only = true; 743 { 744 struct TALER_MerchantPostDataHashP unused; 745 746 /* We need the order for two cases: Either when the contract doesn't exist yet, 747 * or when the order is claimed but unpaid, and we need the claim token. */ 748 qs = TALER_MERCHANTDB_lookup_order (TMH_db, 749 hc->instance->settings.id, 750 hc->infix, 751 &gorc->claim_token, 752 &unused, 753 &gorc->contract_terms_json); 754 } 755 if (0 > qs) 756 { 757 /* single, read-only SQL statements should never cause 758 serialization problems */ 759 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); 760 /* Always report on hard error as well to enable diagnostics */ 761 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); 762 phase_end (gorc, 763 TALER_MHD_reply_with_error (gorc->sc.con, 764 MHD_HTTP_INTERNAL_SERVER_ERROR, 765 TALER_EC_GENERIC_DB_FETCH_FAILED, 766 "order")); 767 return; 768 } 769 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 770 { 771 phase_end (gorc, 772 TALER_MHD_reply_with_error (gorc->sc.con, 773 MHD_HTTP_NOT_FOUND, 774 TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN, 775 hc->infix)); 776 return; 777 } 778 gorc->phase++; 779 } 780 781 782 /** 783 * Obtain parse contract terms of the order. Extracts the fulfillment URL, 784 * total amount, summary and timestamp from the contract terms! 785 * 786 * @param[in,out] gorc order context to update 787 */ 788 static void 789 phase_parse_contract (struct GetOrderRequestContext *gorc) 790 { 791 struct TMH_HandlerContext *hc = gorc->hc; 792 793 if ( (NULL == gorc->order) && 794 (NULL != gorc->order_json) ) 795 { 796 gorc->order = TALER_MERCHANT_order_parse ( 797 gorc->order_json); 798 799 if (NULL == gorc->order) 800 { 801 GNUNET_break (0); 802 phase_end (gorc, 803 TALER_MHD_reply_with_error ( 804 gorc->sc.con, 805 MHD_HTTP_INTERNAL_SERVER_ERROR, 806 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 807 hc->infix)); 808 return; 809 } 810 gorc->ct = gorc->order->base; 811 gorc->timestamp = gorc->order->timestamp; 812 } 813 if ( (NULL == gorc->contract_terms) && 814 (NULL != gorc->contract_terms_json) ) 815 { 816 if (NULL == 817 json_object_get (gorc->contract_terms_json, 818 "nonce")) 819 { 820 /* only have a proto contract */ 821 gorc->pc = TALER_MERCHANT_proto_contract_parse ( 822 gorc->contract_terms_json); 823 824 if (NULL == gorc->pc) 825 { 826 GNUNET_break (0); 827 phase_end (gorc, 828 TALER_MHD_reply_with_error ( 829 gorc->sc.con, 830 MHD_HTTP_INTERNAL_SERVER_ERROR, 831 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 832 hc->infix)); 833 return; 834 } 835 gorc->ct = gorc->pc->base; 836 gorc->timestamp = gorc->pc->timestamp; 837 } 838 else 839 { 840 gorc->contract_terms = TALER_MERCHANT_contract_parse ( 841 gorc->contract_terms_json); 842 if (NULL == gorc->contract_terms) 843 { 844 GNUNET_break (0); 845 phase_end (gorc, 846 TALER_MHD_reply_with_error ( 847 gorc->sc.con, 848 MHD_HTTP_INTERNAL_SERVER_ERROR, 849 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 850 hc->infix)); 851 return; 852 } 853 gorc->pc = gorc->contract_terms->pc; 854 gorc->ct = gorc->pc->base; 855 gorc->timestamp = gorc->contract_terms->pc->timestamp; 856 } 857 } 858 859 switch (gorc->ct->version) 860 { 861 case TALER_MERCHANT_CONTRACT_VERSION_0: 862 gorc->contract_amount 863 = (NULL != gorc->pc) 864 ? gorc->pc->details.v0.brutto 865 : gorc->order->details.v0.brutto; 866 break; 867 case TALER_MERCHANT_CONTRACT_VERSION_1: 868 if (gorc->choice_index >= 0) 869 { 870 if (gorc->choice_index >= 871 ( (NULL != gorc->pc) 872 ? gorc->pc->details.v1.choices_len 873 : gorc->order->details.v1.choices_len) ) 874 { 875 GNUNET_break (0); 876 phase_end (gorc, 877 TALER_MHD_reply_with_error ( 878 gorc->sc.con, 879 MHD_HTTP_INTERNAL_SERVER_ERROR, 880 TALER_EC_GENERIC_DB_INVARIANT_FAILURE, 881 NULL)); 882 return; 883 } 884 gorc->contract_amount = 885 (NULL != gorc->pc) 886 ? gorc->pc->details.v1.choices[gorc->choice_index].amount 887 : gorc->order->details.v1.choices[gorc->choice_index].amount; 888 } 889 else 890 { 891 GNUNET_break (gorc->order_only); 892 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 893 "Choice index %i is invalid", 894 gorc->choice_index); 895 } 896 break; 897 default: 898 { 899 GNUNET_break (0); 900 phase_end (gorc, 901 TALER_MHD_reply_with_error ( 902 gorc->sc.con, 903 MHD_HTTP_INTERNAL_SERVER_ERROR, 904 TALER_EC_MERCHANT_GET_ORDERS_ID_INVALID_CONTRACT_VERSION, 905 NULL)); 906 return; 907 } 908 } 909 910 if ( (NULL != gorc->contract_terms) && 911 (GNUNET_OK != 912 TALER_JSON_contract_hash (gorc->contract_terms_json, 913 &gorc->h_contract_terms)) ) 914 { 915 GNUNET_break (0); 916 phase_end (gorc, 917 TALER_MHD_reply_with_error (gorc->sc.con, 918 MHD_HTTP_INTERNAL_SERVER_ERROR, 919 TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH, 920 NULL)); 921 return; 922 } 923 GNUNET_assert ( (NULL != gorc->contract_terms_json) || 924 (NULL != gorc->order_json) ); 925 GNUNET_assert ( (NULL != gorc->pc) || 926 (NULL != gorc->order) ); 927 gorc->phase++; 928 } 929 930 931 /** 932 * Check payment status of the order. 933 * 934 * @param[in,out] gorc order context to update 935 */ 936 static void 937 phase_check_paid (struct GetOrderRequestContext *gorc) 938 { 939 struct TMH_HandlerContext *hc = gorc->hc; 940 941 if (gorc->order_only) 942 { 943 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 944 "Order %s unclaimed, no need to lookup payment status\n", 945 hc->infix); 946 GNUNET_assert (! gorc->paid); 947 GNUNET_assert (! gorc->wired); 948 gorc->phase++; 949 return; 950 } 951 if (NULL == gorc->session_id) 952 { 953 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 954 "No session ID, do not need to lookup session-ID specific payment status (%s/%s)\n", 955 gorc->paid ? "paid" : "unpaid", 956 gorc->wired ? "wired" : "unwired"); 957 gorc->phase++; 958 return; 959 } 960 if (! gorc->paid_session_matches) 961 { 962 gorc->paid = false; 963 gorc->wired = false; 964 } 965 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 966 "Order %s %s for session %s (%s)\n", 967 hc->infix, 968 gorc->paid ? "paid" : "unpaid", 969 gorc->session_id, 970 gorc->wired ? "wired" : "unwired"); 971 gorc->phase++; 972 } 973 974 975 /** 976 * Check if the @a reply satisfies the long-poll not_etag 977 * constraint. If so, return it as a response for @a gorc, 978 * otherwise suspend and wait for a change. 979 * 980 * @param[in,out] gorc request to handle 981 * @param reply body for JSON response (#MHD_HTTP_OK) 982 */ 983 static void 984 check_reply (struct GetOrderRequestContext *gorc, 985 const json_t *reply) 986 { 987 struct GNUNET_ShortHashCode sh; 988 unsigned int http_response_code; 989 bool not_modified; 990 struct MHD_Response *response; 991 char *can; 992 993 can = TALER_JSON_canonicalize (reply); 994 GNUNET_assert (GNUNET_YES == 995 GNUNET_CRYPTO_hkdf_gnunet (&sh, 996 sizeof (sh), 997 "GOR-SALT", 998 strlen ("GOR-SALT"), 999 can, 1000 strlen (can))); 1001 not_modified = gorc->have_lp_not_etag && 1002 (0 == GNUNET_memcmp (&sh, 1003 &gorc->lp_not_etag)); 1004 1005 if (not_modified && 1006 (! GNUNET_TIME_absolute_is_past (gorc->sc.long_poll_timeout)) ) 1007 { 1008 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1009 "Status unchanged, not returning response yet\n"); 1010 GNUNET_assert (GNUNET_NO == gorc->suspended); 1011 /* note: not necessarily actually unpaid ... */ 1012 GNUNET_CONTAINER_DLL_insert (gorc_head, 1013 gorc_tail, 1014 gorc); 1015 gorc->phase = GOP_SUSPENDED_ON_UNPAID; 1016 gorc->suspended = GNUNET_YES; 1017 MHD_suspend_connection (gorc->sc.con); 1018 GNUNET_free (can); 1019 return; 1020 } 1021 { 1022 const char *inm; 1023 1024 inm = MHD_lookup_connection_value (gorc->sc.con, 1025 MHD_GET_ARGUMENT_KIND, 1026 MHD_HTTP_HEADER_IF_NONE_MATCH); 1027 if ( (NULL == inm) || 1028 ('"' != inm[0]) || 1029 ('"' != inm[strlen (inm) - 1]) || 1030 (0 != strncmp (inm + 1, 1031 can, 1032 strlen (can))) ) 1033 not_modified = false; /* must return full response */ 1034 } 1035 GNUNET_free (can); 1036 http_response_code = not_modified 1037 ? MHD_HTTP_NOT_MODIFIED 1038 : MHD_HTTP_OK; 1039 response = TALER_MHD_make_json (reply); 1040 { 1041 char *etag; 1042 char *qetag; 1043 1044 etag = GNUNET_STRINGS_data_to_string_alloc (&sh, 1045 sizeof (sh)); 1046 GNUNET_asprintf (&qetag, 1047 "\"%s\"", 1048 etag); 1049 GNUNET_break (MHD_YES == 1050 MHD_add_response_header (response, 1051 MHD_HTTP_HEADER_ETAG, 1052 qetag)); 1053 GNUNET_free (qetag); 1054 GNUNET_free (etag); 1055 } 1056 1057 { 1058 enum MHD_Result ret; 1059 1060 ret = MHD_queue_response (gorc->sc.con, 1061 http_response_code, 1062 response); 1063 MHD_destroy_response (response); 1064 phase_end (gorc, 1065 ret); 1066 } 1067 } 1068 1069 1070 /** 1071 * Check if re-purchase detection applies to the order. 1072 * 1073 * @param[in,out] gorc order context to update 1074 */ 1075 static void 1076 phase_check_repurchase (struct GetOrderRequestContext *gorc) 1077 { 1078 struct TMH_HandlerContext *hc = gorc->hc; 1079 char *already_paid_order_id = NULL; 1080 enum GNUNET_DB_QueryStatus qs; 1081 char *taler_pay_uri; 1082 char *order_status_url; 1083 json_t *reply; 1084 1085 if ( (gorc->paid) || 1086 (NULL == gorc->ct->fulfillment_url) || 1087 (NULL == gorc->session_id) ) 1088 { 1089 /* Repurchase cannot apply */ 1090 gorc->phase++; 1091 return; 1092 } 1093 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1094 "Running re-purchase detection for %s/%s\n", 1095 gorc->session_id, 1096 gorc->ct->fulfillment_url); 1097 qs = TALER_MERCHANTDB_lookup_order_by_fulfillment ( 1098 TMH_db, 1099 hc->instance->settings.id, 1100 gorc->ct->fulfillment_url, 1101 gorc->session_id, 1102 TALER_EXCHANGE_YNA_NO != 1103 gorc->allow_refunded_for_repurchase, 1104 &already_paid_order_id); 1105 if (0 > qs) 1106 { 1107 /* single, read-only SQL statements should never cause 1108 serialization problems, and the entry should exist as per above */ 1109 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); 1110 phase_end (gorc, 1111 TALER_MHD_reply_with_error (gorc->sc.con, 1112 MHD_HTTP_INTERNAL_SERVER_ERROR, 1113 TALER_EC_GENERIC_DB_FETCH_FAILED, 1114 "order by fulfillment")); 1115 return; 1116 } 1117 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 1118 { 1119 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1120 "No already paid order for %s/%s\n", 1121 gorc->session_id, 1122 gorc->ct->fulfillment_url); 1123 gorc->phase++; 1124 return; 1125 } 1126 1127 /* User did pay for this order, but under a different session; ask wallet to 1128 switch order ID */ 1129 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1130 "Found already paid order %s\n", 1131 already_paid_order_id); 1132 taler_pay_uri = TMH_make_taler_pay_uri (gorc->sc.con, 1133 hc->infix, 1134 gorc->session_id, 1135 hc->instance->settings.id, 1136 &gorc->claim_token); 1137 order_status_url = TMH_make_order_status_url (gorc->sc.con, 1138 hc->infix, 1139 gorc->session_id, 1140 hc->instance->settings.id, 1141 &gorc->claim_token, 1142 NULL); 1143 if ( (NULL == taler_pay_uri) || 1144 (NULL == order_status_url) ) 1145 { 1146 GNUNET_break_op (0); 1147 GNUNET_free (taler_pay_uri); 1148 GNUNET_free (order_status_url); 1149 phase_end (gorc, 1150 TALER_MHD_reply_with_error (gorc->sc.con, 1151 MHD_HTTP_BAD_REQUEST, 1152 TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED, 1153 "host")); 1154 return; 1155 } 1156 reply = GNUNET_JSON_PACK ( 1157 GNUNET_JSON_pack_string ("taler_pay_uri", 1158 taler_pay_uri), 1159 GNUNET_JSON_pack_string ("order_status_url", 1160 order_status_url), 1161 GNUNET_JSON_pack_string ("order_status", 1162 "unpaid"), 1163 GNUNET_JSON_pack_string ("already_paid_order_id", 1164 already_paid_order_id), 1165 GNUNET_JSON_pack_string ("already_paid_fulfillment_url", 1166 gorc->ct->fulfillment_url), 1167 /* undefined for unpaid v1 contracts */ 1168 GNUNET_JSON_pack_allow_null ( 1169 TALER_JSON_pack_amount ("total_amount", 1170 TALER_amount_is_valid (&gorc->contract_amount) 1171 ? &gorc->contract_amount 1172 : NULL)), 1173 GNUNET_JSON_pack_object_incref ("proto_contract_terms", 1174 gorc->contract_terms_json), 1175 GNUNET_JSON_pack_string ("summary", 1176 gorc->ct->summary), 1177 GNUNET_JSON_pack_timestamp ("pay_deadline", 1178 NULL != gorc->pc 1179 ? gorc->pc->pay_deadline 1180 : gorc->order->pay_deadline), 1181 GNUNET_JSON_pack_timestamp ("creation_time", 1182 gorc->timestamp)); 1183 1184 GNUNET_free (order_status_url); 1185 GNUNET_free (taler_pay_uri); 1186 GNUNET_free (already_paid_order_id); 1187 check_reply (gorc, 1188 reply); 1189 json_decref (reply); 1190 } 1191 1192 1193 /** 1194 * Check if we should suspend until the order is paid. 1195 * 1196 * @param[in,out] gorc order context to update 1197 */ 1198 static void 1199 phase_unpaid_finish (struct GetOrderRequestContext *gorc) 1200 { 1201 struct TMH_HandlerContext *hc = gorc->hc; 1202 char *order_status_url; 1203 1204 if (gorc->paid) 1205 { 1206 gorc->phase++; 1207 return; 1208 } 1209 /* User never paid for this order, suspend waiting 1210 on payment or return details. */ 1211 if (GNUNET_TIME_absolute_is_future (gorc->sc.long_poll_timeout) && 1212 (! gorc->have_lp_not_etag) ) 1213 { 1214 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1215 "Suspending GET /private/orders/%s\n", 1216 hc->infix); 1217 GNUNET_CONTAINER_DLL_insert (gorc_head, 1218 gorc_tail, 1219 gorc); 1220 gorc->phase = GOP_SUSPENDED_ON_UNPAID; 1221 gorc->suspended = GNUNET_YES; 1222 MHD_suspend_connection (gorc->sc.con); 1223 return; 1224 } 1225 order_status_url = TMH_make_order_status_url (gorc->sc.con, 1226 hc->infix, 1227 gorc->session_id, 1228 hc->instance->settings.id, 1229 &gorc->claim_token, 1230 NULL); 1231 if (! gorc->order_only) 1232 { 1233 json_t *reply; 1234 1235 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1236 "Order %s claimed but not paid yet\n", 1237 hc->infix); 1238 reply = GNUNET_JSON_PACK ( 1239 GNUNET_JSON_pack_string ("order_status_url", 1240 order_status_url), 1241 GNUNET_JSON_pack_object_incref ("contract_terms", 1242 gorc->contract_terms_json), 1243 GNUNET_JSON_pack_string ("order_status", 1244 "claimed")); 1245 GNUNET_free (order_status_url); 1246 check_reply (gorc, 1247 reply); 1248 json_decref (reply); 1249 return; 1250 } 1251 { 1252 char *taler_pay_uri; 1253 json_t *reply; 1254 1255 taler_pay_uri = TMH_make_taler_pay_uri (gorc->sc.con, 1256 hc->infix, 1257 gorc->session_id, 1258 hc->instance->settings.id, 1259 &gorc->claim_token); 1260 reply = GNUNET_JSON_PACK ( 1261 GNUNET_JSON_pack_string ("taler_pay_uri", 1262 taler_pay_uri), 1263 GNUNET_JSON_pack_string ("order_status_url", 1264 order_status_url), 1265 GNUNET_JSON_pack_string ("order_status", 1266 "unpaid"), 1267 GNUNET_JSON_pack_object_incref ("proto_contract_terms", 1268 gorc->contract_terms_json), 1269 /* undefined for unpaid v1 contracts */ 1270 GNUNET_JSON_pack_allow_null ( 1271 TALER_JSON_pack_amount ("total_amount", 1272 &gorc->contract_amount)), 1273 GNUNET_JSON_pack_string ("summary", 1274 gorc->ct->summary), 1275 GNUNET_JSON_pack_timestamp ("creation_time", 1276 gorc->timestamp)); 1277 check_reply (gorc, 1278 reply); 1279 json_decref (reply); 1280 GNUNET_free (taler_pay_uri); 1281 } 1282 GNUNET_free (order_status_url); 1283 } 1284 1285 1286 /** 1287 * Function called with information about a refund. 1288 * It is responsible for summing up the refund amount. 1289 * 1290 * @param cls closure 1291 * @param refund_serial unique serial number of the refund 1292 * @param timestamp time of the refund (for grouping of refunds in the wallet UI) 1293 * @param coin_pub public coin from which the refund comes from 1294 * @param exchange_url URL of the exchange that issued @a coin_pub 1295 * @param rtransaction_id identificator of the refund 1296 * @param reason human-readable explanation of the refund 1297 * @param refund_amount refund amount which is being taken from @a coin_pub 1298 * @param pending true if the this refund was not yet processed by the wallet/exchange 1299 */ 1300 static void 1301 process_refunds_cb ( 1302 void *cls, 1303 uint64_t refund_serial, 1304 struct GNUNET_TIME_Timestamp timestamp, 1305 const struct TALER_CoinSpendPublicKeyP *coin_pub, 1306 const char *exchange_url, 1307 uint64_t rtransaction_id, 1308 const char *reason, 1309 const struct TALER_Amount *refund_amount, 1310 bool pending) 1311 { 1312 struct GetOrderRequestContext *gorc = cls; 1313 1314 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1315 "Found refund %llu over %s for reason %s\n", 1316 (unsigned long long) rtransaction_id, 1317 TALER_amount2s (refund_amount), 1318 reason); 1319 GNUNET_assert ( 1320 0 == 1321 json_array_append_new ( 1322 gorc->refund_details, 1323 GNUNET_JSON_PACK ( 1324 TALER_JSON_pack_amount ("amount", 1325 refund_amount), 1326 GNUNET_JSON_pack_bool ("pending", 1327 pending), 1328 GNUNET_JSON_pack_timestamp ("timestamp", 1329 timestamp), 1330 GNUNET_JSON_pack_string ("reason", 1331 reason)))); 1332 /* For refunded coins, we are not charged deposit fees, so subtract those 1333 again */ 1334 for (struct TransferQuery *tq = gorc->tq_head; 1335 NULL != tq; 1336 tq = tq->next) 1337 { 1338 if (0 != 1339 strcmp (exchange_url, 1340 tq->exchange_url)) 1341 continue; 1342 if (0 != 1343 GNUNET_memcmp (&tq->coin_pub, 1344 coin_pub)) 1345 continue; 1346 if (GNUNET_OK != 1347 TALER_amount_cmp_currency ( 1348 &gorc->deposit_fees_total, 1349 &tq->deposit_fee)) 1350 { 1351 gorc->refund_currency_mismatch = true; 1352 return; 1353 } 1354 GNUNET_assert ( 1355 0 <= 1356 TALER_amount_add (&gorc->deposit_fees_refunded_total, 1357 &gorc->deposit_fees_refunded_total, 1358 &tq->deposit_fee)); 1359 } 1360 if (GNUNET_OK != 1361 TALER_amount_cmp_currency ( 1362 &gorc->refund_amount, 1363 refund_amount)) 1364 { 1365 gorc->refund_currency_mismatch = true; 1366 return; 1367 } 1368 GNUNET_assert (0 <= 1369 TALER_amount_add (&gorc->refund_amount, 1370 &gorc->refund_amount, 1371 refund_amount)); 1372 gorc->refunded = true; 1373 gorc->refund_pending |= pending; 1374 } 1375 1376 1377 /** 1378 * Check refund status for the order. 1379 * 1380 * @param[in,out] gorc order context to update 1381 */ 1382 static void 1383 phase_check_refunds (struct GetOrderRequestContext *gorc) 1384 { 1385 struct TMH_HandlerContext *hc = gorc->hc; 1386 enum GNUNET_DB_QueryStatus qs; 1387 1388 GNUNET_assert (! gorc->order_only); 1389 GNUNET_assert (gorc->paid); 1390 1391 /* Accumulate refunds, if any. */ 1392 GNUNET_assert (GNUNET_OK == 1393 TALER_amount_set_zero (gorc->contract_amount.currency, 1394 &gorc->refund_amount)); 1395 json_array_clear (gorc->refund_details); 1396 qs = TALER_MERCHANTDB_lookup_refunds_detailed ( 1397 TMH_db, 1398 hc->instance->settings.id, 1399 &gorc->h_contract_terms, 1400 &process_refunds_cb, 1401 gorc); 1402 if (0 > qs) 1403 { 1404 GNUNET_break (0); 1405 phase_end (gorc, 1406 TALER_MHD_reply_with_error ( 1407 gorc->sc.con, 1408 MHD_HTTP_INTERNAL_SERVER_ERROR, 1409 TALER_EC_GENERIC_DB_FETCH_FAILED, 1410 "detailed refunds")); 1411 return; 1412 } 1413 if (gorc->refund_currency_mismatch) 1414 { 1415 GNUNET_break (0); 1416 phase_end (gorc, 1417 TALER_MHD_reply_with_error ( 1418 gorc->sc.con, 1419 MHD_HTTP_INTERNAL_SERVER_ERROR, 1420 TALER_EC_GENERIC_DB_FETCH_FAILED, 1421 "refunds in different currency than original order price")); 1422 return; 1423 } 1424 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1425 "Total refunds are %s\n", 1426 TALER_amount2s (&gorc->refund_amount)); 1427 gorc->phase++; 1428 } 1429 1430 1431 /** 1432 * Function called with each @a coin_pub that was deposited into the 1433 * @a h_wire account of the merchant for the @a deposit_serial as part 1434 * of the payment for the order identified by @a cls. 1435 * 1436 * Queries the exchange for the payment status associated with the 1437 * given coin. 1438 * 1439 * @param cls a `struct GetOrderRequestContext` 1440 * @param deposit_serial identifies the deposit operation 1441 * @param exchange_url URL of the exchange that issued @a coin_pub 1442 * @param h_wire hash of the merchant's wire account into which the deposit was made 1443 * @param deposit_timestamp when was the deposit made 1444 * @param amount_with_fee amount the exchange will deposit for this coin 1445 * @param deposit_fee fee the exchange will charge for this coin 1446 * @param coin_pub public key of the deposited coin 1447 */ 1448 static void 1449 deposit_cb ( 1450 void *cls, 1451 uint64_t deposit_serial, 1452 const char *exchange_url, 1453 const struct TALER_MerchantWireHashP *h_wire, 1454 struct GNUNET_TIME_Timestamp deposit_timestamp, 1455 const struct TALER_Amount *amount_with_fee, 1456 const struct TALER_Amount *deposit_fee, 1457 const struct TALER_CoinSpendPublicKeyP *coin_pub) 1458 { 1459 struct GetOrderRequestContext *gorc = cls; 1460 struct TransferQuery *tq; 1461 1462 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1463 "Checking deposit status for coin %s (over %s)\n", 1464 TALER_B2S (coin_pub), 1465 TALER_amount2s (amount_with_fee)); 1466 gorc->last_payment 1467 = GNUNET_TIME_timestamp_max (gorc->last_payment, 1468 deposit_timestamp); 1469 tq = GNUNET_new (struct TransferQuery); 1470 tq->gorc = gorc; 1471 tq->exchange_url = GNUNET_strdup (exchange_url); 1472 tq->deposit_serial = deposit_serial; 1473 GNUNET_CONTAINER_DLL_insert (gorc->tq_head, 1474 gorc->tq_tail, 1475 tq); 1476 tq->coin_pub = *coin_pub; 1477 tq->h_wire = *h_wire; 1478 tq->amount_with_fee = *amount_with_fee; 1479 tq->deposit_fee = *deposit_fee; 1480 } 1481 1482 1483 /** 1484 * Check wire transfer status for the order at the exchange. 1485 * 1486 * @param[in,out] gorc order context to update 1487 */ 1488 static void 1489 phase_check_deposits (struct GetOrderRequestContext *gorc) 1490 { 1491 GNUNET_assert (! gorc->order_only); 1492 GNUNET_assert (gorc->paid); 1493 1494 /* amount must be always valid for paid orders */ 1495 GNUNET_assert (GNUNET_OK == 1496 TALER_amount_is_valid (&gorc->contract_amount)); 1497 1498 GNUNET_assert (GNUNET_OK == 1499 TALER_amount_set_zero (gorc->contract_amount.currency, 1500 &gorc->deposits_total)); 1501 GNUNET_assert (GNUNET_OK == 1502 TALER_amount_set_zero (gorc->contract_amount.currency, 1503 &gorc->deposit_fees_total)); 1504 GNUNET_assert (GNUNET_OK == 1505 TALER_amount_set_zero (gorc->contract_amount.currency, 1506 &gorc->deposit_fees_refunded_total)); 1507 TALER_MERCHANTDB_lookup_deposits_by_order (TMH_db, 1508 gorc->order_serial, 1509 &deposit_cb, 1510 gorc); 1511 gorc->phase++; 1512 } 1513 1514 1515 /** 1516 * Function called with available wire details, to be added to 1517 * the response. 1518 * 1519 * @param cls a `struct GetOrderRequestContext` 1520 * @param wtid wire transfer subject of the wire transfer for the coin 1521 * @param exchange_url base URL of the exchange that made the payment 1522 * @param execution_time when was the payment made 1523 * @param deposit_value contribution of the coin to the total wire transfer value 1524 * @param deposit_fee deposit fee charged by the exchange for the coin 1525 * @param transfer_confirmed did the merchant confirm that a wire transfer with 1526 * @a wtid over the total amount happened? 1527 * @param expected_credit_serial row for the expected wire transfer this 1528 * entry references 1529 */ 1530 static void 1531 process_transfer_details ( 1532 void *cls, 1533 const struct TALER_WireTransferIdentifierRawP *wtid, 1534 const char *exchange_url, 1535 struct GNUNET_TIME_Timestamp execution_time, 1536 const struct TALER_Amount *deposit_value, 1537 const struct TALER_Amount *deposit_fee, 1538 bool transfer_confirmed, 1539 uint64_t expected_credit_serial) 1540 { 1541 struct GetOrderRequestContext *gorc = cls; 1542 json_t *wire_details = gorc->wire_details; 1543 struct TALER_Amount wired; 1544 1545 if ( (GNUNET_OK != 1546 TALER_amount_cmp_currency (&gorc->deposits_total, 1547 deposit_value)) || 1548 (GNUNET_OK != 1549 TALER_amount_cmp_currency (&gorc->deposit_fees_total, 1550 deposit_fee)) ) 1551 { 1552 GNUNET_break (0); 1553 gorc->deposit_currency_mismatch = true; 1554 return; 1555 } 1556 1557 /* Compute total amount *wired* */ 1558 GNUNET_assert (0 <= 1559 TALER_amount_add (&gorc->deposits_total, 1560 &gorc->deposits_total, 1561 deposit_value)); 1562 GNUNET_assert (0 <= 1563 TALER_amount_add (&gorc->deposit_fees_total, 1564 &gorc->deposit_fees_total, 1565 deposit_fee)); 1566 GNUNET_assert (0 <= TALER_amount_subtract (&wired, 1567 deposit_value, 1568 deposit_fee)); 1569 GNUNET_assert (0 == 1570 json_array_append_new ( 1571 wire_details, 1572 GNUNET_JSON_PACK ( 1573 GNUNET_JSON_pack_data_auto ("wtid", 1574 wtid), 1575 GNUNET_JSON_pack_string ("exchange_url", 1576 exchange_url), 1577 TALER_JSON_pack_amount ("amount", 1578 &wired), 1579 TALER_JSON_pack_amount ("deposit_fee", 1580 deposit_fee), 1581 GNUNET_JSON_pack_timestamp ("execution_time", 1582 execution_time), 1583 GNUNET_JSON_pack_bool ("confirmed", 1584 transfer_confirmed), 1585 GNUNET_JSON_pack_uint64 ("expected_transfer_serial_id", 1586 expected_credit_serial)))); 1587 } 1588 1589 1590 /** 1591 * Check transfer status in local database. 1592 * 1593 * @param[in,out] gorc order context to update 1594 */ 1595 static void 1596 phase_check_local_transfers (struct GetOrderRequestContext *gorc) 1597 { 1598 struct TMH_HandlerContext *hc = gorc->hc; 1599 enum GNUNET_DB_QueryStatus qs; 1600 1601 GNUNET_assert (gorc->paid); 1602 GNUNET_assert (! gorc->order_only); 1603 1604 GNUNET_assert (GNUNET_OK == 1605 TALER_amount_set_zero (gorc->contract_amount.currency, 1606 &gorc->deposits_total)); 1607 GNUNET_assert (GNUNET_OK == 1608 TALER_amount_set_zero (gorc->contract_amount.currency, 1609 &gorc->deposit_fees_total)); 1610 GNUNET_assert (NULL != gorc->wire_details); 1611 /* We may be running again due to long-polling, clear state first */ 1612 json_array_clear (gorc->wire_details); 1613 qs = TALER_MERCHANTDB_lookup_transfer_details_by_order (TMH_db, 1614 gorc->order_serial, 1615 &process_transfer_details, 1616 gorc); 1617 if (0 > qs) 1618 { 1619 GNUNET_break (0); 1620 phase_end (gorc, 1621 TALER_MHD_reply_with_error (gorc->sc.con, 1622 MHD_HTTP_INTERNAL_SERVER_ERROR, 1623 TALER_EC_GENERIC_DB_FETCH_FAILED, 1624 "transfer details")); 1625 return; 1626 } 1627 if (gorc->deposit_currency_mismatch) 1628 { 1629 GNUNET_break (0); 1630 phase_end (gorc, 1631 TALER_MHD_reply_with_error (gorc->sc.con, 1632 MHD_HTTP_INTERNAL_SERVER_ERROR, 1633 TALER_EC_GENERIC_DB_FETCH_FAILED, 1634 "deposits in different currency than original order price")); 1635 return; 1636 } 1637 1638 if (! gorc->wired) 1639 { 1640 /* we believe(d) the wire transfer did not happen yet, check if maybe 1641 in light of new evidence it did */ 1642 struct TALER_Amount expect_total; 1643 1644 if (0 > 1645 TALER_amount_subtract (&expect_total, 1646 &gorc->contract_amount, 1647 &gorc->refund_amount)) 1648 { 1649 GNUNET_break (0); 1650 phase_end (gorc, 1651 TALER_MHD_reply_with_error ( 1652 gorc->sc.con, 1653 MHD_HTTP_INTERNAL_SERVER_ERROR, 1654 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 1655 "refund exceeds contract value")); 1656 return; 1657 } 1658 GNUNET_assert ( 1659 0 <= 1660 TALER_amount_add (&expect_total, 1661 &expect_total, 1662 &gorc->deposit_fees_refunded_total)); 1663 1664 if (0 > 1665 TALER_amount_subtract (&expect_total, 1666 &expect_total, 1667 &gorc->deposit_fees_total)) 1668 { 1669 GNUNET_break (0); 1670 phase_end (gorc, 1671 TALER_MHD_reply_with_error ( 1672 gorc->sc.con, 1673 MHD_HTTP_INTERNAL_SERVER_ERROR, 1674 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 1675 "deposit fees exceed total minus refunds")); 1676 return; 1677 } 1678 if (0 >= 1679 TALER_amount_cmp (&expect_total, 1680 &gorc->deposits_total)) 1681 { 1682 /* expect_total <= gorc->deposits_total: good: we got the wire transfer */ 1683 gorc->wired = true; 1684 qs = TALER_MERCHANTDB_mark_order_wired (TMH_db, 1685 gorc->order_serial); 1686 GNUNET_break (qs >= 0); /* just warn if transaction failed */ 1687 TMH_notify_order_change (hc->instance, 1688 TMH_OSF_PAID 1689 | TMH_OSF_WIRED, 1690 gorc->timestamp, 1691 gorc->order_serial); 1692 } 1693 } 1694 gorc->phase++; 1695 } 1696 1697 1698 /** 1699 * Generate final result for the status request. 1700 * 1701 * @param[in,out] gorc order context to update 1702 */ 1703 static void 1704 phase_reply_result (struct GetOrderRequestContext *gorc) 1705 { 1706 struct TMH_HandlerContext *hc = gorc->hc; 1707 char *order_status_url; 1708 1709 GNUNET_assert (gorc->paid); 1710 GNUNET_assert (! gorc->order_only); 1711 1712 { 1713 struct TALER_PrivateContractHashP *h_contract = NULL; 1714 1715 /* In a session-bound payment, allow the browser to check the order 1716 * status page (e.g. to get a refund). 1717 * 1718 * Note that we don't allow this outside of session-based payment, as 1719 * otherwise this becomes an oracle to convert order_id to h_contract. 1720 */ 1721 if (NULL != gorc->session_id) 1722 h_contract = &gorc->h_contract_terms; 1723 1724 order_status_url = 1725 TMH_make_order_status_url (gorc->sc.con, 1726 hc->infix, 1727 gorc->session_id, 1728 hc->instance->settings.id, 1729 &gorc->claim_token, 1730 h_contract); 1731 } 1732 if (GNUNET_TIME_absolute_is_zero (gorc->last_payment.abs_time)) 1733 { 1734 GNUNET_break (GNUNET_YES == 1735 TALER_amount_is_zero (&gorc->contract_amount)); 1736 gorc->last_payment = gorc->timestamp; 1737 } 1738 { 1739 json_t *reply; 1740 1741 reply = GNUNET_JSON_PACK ( 1742 // Deprecated in protocol v6! 1743 GNUNET_JSON_pack_array_steal ("wire_reports", 1744 json_array ()), 1745 GNUNET_JSON_pack_uint64 ("exchange_code", 1746 gorc->exchange_ec), 1747 GNUNET_JSON_pack_uint64 ("exchange_http_status", 1748 gorc->exchange_hc), 1749 /* legacy: */ 1750 GNUNET_JSON_pack_uint64 ("exchange_ec", 1751 gorc->exchange_ec), 1752 /* legacy: */ 1753 GNUNET_JSON_pack_uint64 ("exchange_hc", 1754 gorc->exchange_hc), 1755 TALER_JSON_pack_amount ("deposit_total", 1756 &gorc->deposits_total), 1757 GNUNET_JSON_pack_object_incref ("contract_terms", 1758 gorc->contract_terms_json), 1759 GNUNET_JSON_pack_string ("order_status", 1760 "paid"), 1761 GNUNET_JSON_pack_timestamp ("last_payment", 1762 gorc->last_payment), 1763 GNUNET_JSON_pack_bool ("refunded", 1764 gorc->refunded), 1765 GNUNET_JSON_pack_bool ("wired", 1766 gorc->wired), 1767 GNUNET_JSON_pack_bool ("refund_pending", 1768 gorc->refund_pending), 1769 GNUNET_JSON_pack_allow_null ( 1770 TALER_JSON_pack_amount ("refund_amount", 1771 &gorc->refund_amount)), 1772 GNUNET_JSON_pack_array_incref ("wire_details", 1773 gorc->wire_details), 1774 GNUNET_JSON_pack_array_incref ("refund_details", 1775 gorc->refund_details), 1776 GNUNET_JSON_pack_string ("order_status_url", 1777 order_status_url), 1778 (gorc->choice_index >= 0) 1779 ? GNUNET_JSON_pack_int64 ("choice_index", 1780 gorc->choice_index) 1781 : GNUNET_JSON_pack_end_ ()); 1782 check_reply (gorc, 1783 reply); 1784 json_decref (reply); 1785 } 1786 GNUNET_free (order_status_url); 1787 } 1788 1789 1790 /** 1791 * End with error status in wire_hc and wire_ec. 1792 * 1793 * @param[in,out] gorc order context to update 1794 */ 1795 static void 1796 phase_error (struct GetOrderRequestContext *gorc) 1797 { 1798 GNUNET_assert (TALER_EC_NONE != gorc->wire_ec); 1799 phase_end (gorc, 1800 TALER_MHD_reply_with_error (gorc->sc.con, 1801 gorc->wire_hc, 1802 gorc->wire_ec, 1803 NULL)); 1804 } 1805 1806 1807 enum MHD_Result 1808 TMH_private_get_orders_ID ( 1809 const struct TMH_RequestHandler *rh, 1810 struct MHD_Connection *connection, 1811 struct TMH_HandlerContext *hc) 1812 { 1813 struct GetOrderRequestContext *gorc = hc->ctx; 1814 1815 if (NULL == gorc) 1816 { 1817 /* First time here, parse request and check order is known */ 1818 GNUNET_assert (NULL != hc->infix); 1819 gorc = GNUNET_new (struct GetOrderRequestContext); 1820 hc->cc = &gorc_cleanup; 1821 hc->ctx = gorc; 1822 gorc->sc.con = connection; 1823 gorc->hc = hc; 1824 gorc->wire_details = json_array (); 1825 GNUNET_assert (NULL != gorc->wire_details); 1826 gorc->refund_details = json_array (); 1827 GNUNET_assert (NULL != gorc->refund_details); 1828 gorc->session_id = MHD_lookup_connection_value (connection, 1829 MHD_GET_ARGUMENT_KIND, 1830 "session_id"); 1831 if (! (TALER_MHD_arg_to_yna (connection, 1832 "allow_refunded_for_repurchase", 1833 TALER_EXCHANGE_YNA_NO, 1834 &gorc->allow_refunded_for_repurchase)) ) 1835 return TALER_MHD_reply_with_error (connection, 1836 MHD_HTTP_BAD_REQUEST, 1837 TALER_EC_GENERIC_PARAMETER_MALFORMED, 1838 "allow_refunded_for_repurchase"); 1839 TALER_MHD_parse_request_timeout (connection, 1840 &gorc->sc.long_poll_timeout); 1841 TALER_MHD_parse_request_arg_auto (connection, 1842 "lp_not_etag", 1843 &gorc->lp_not_etag, 1844 gorc->have_lp_not_etag); 1845 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1846 "Starting GET /private/orders/%s processing with timeout %s\n", 1847 hc->infix, 1848 GNUNET_STRINGS_absolute_time_to_string ( 1849 gorc->sc.long_poll_timeout)); 1850 } 1851 if (GNUNET_SYSERR == gorc->suspended) 1852 return MHD_NO; /* we are in shutdown */ 1853 while (1) 1854 { 1855 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1856 "Processing order %s in phase %d\n", 1857 hc->infix, 1858 (int) gorc->phase); 1859 switch (gorc->phase) 1860 { 1861 case GOP_INIT: 1862 phase_init (gorc); 1863 break; 1864 case GOP_FETCH_CONTRACT: 1865 phase_fetch_contract (gorc); 1866 break; 1867 case GOP_PARSE_CONTRACT: 1868 phase_parse_contract (gorc); 1869 break; 1870 case GOP_CHECK_PAID: 1871 phase_check_paid (gorc); 1872 break; 1873 case GOP_CHECK_REPURCHASE: 1874 phase_check_repurchase (gorc); 1875 break; 1876 case GOP_UNPAID_FINISH: 1877 phase_unpaid_finish (gorc); 1878 break; 1879 case GOP_CHECK_REFUNDS: 1880 phase_check_refunds (gorc); 1881 break; 1882 case GOP_CHECK_DEPOSITS: 1883 phase_check_deposits (gorc); 1884 break; 1885 case GOP_CHECK_LOCAL_TRANSFERS: 1886 phase_check_local_transfers (gorc); 1887 break; 1888 case GOP_REPLY_RESULT: 1889 phase_reply_result (gorc); 1890 break; 1891 case GOP_ERROR: 1892 phase_error (gorc); 1893 break; 1894 case GOP_SUSPENDED_ON_UNPAID: 1895 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1896 "Suspending order request awaiting payment\n"); 1897 return MHD_YES; 1898 case GOP_END_YES: 1899 return MHD_YES; 1900 case GOP_END_NO: 1901 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1902 "Closing connection, no response generated\n"); 1903 return MHD_NO; 1904 } 1905 } /* end first-time per-request initialization */ 1906 }