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