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