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