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