taler-merchant-httpd_get-private-orders-ORDER_ID.c (62895B)
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_log (GNUNET_ERROR_TYPE_INFO, 920 "Choice index %i is invalid", 921 gorc->choice_index); 922 } 923 break; 924 default: 925 { 926 GNUNET_break (0); 927 phase_end (gorc, 928 TALER_MHD_reply_with_error ( 929 gorc->sc.con, 930 MHD_HTTP_INTERNAL_SERVER_ERROR, 931 TALER_EC_MERCHANT_GET_ORDERS_ID_INVALID_CONTRACT_VERSION, 932 NULL)); 933 return; 934 } 935 } 936 937 if ( (NULL != gorc->contract_terms) && 938 (GNUNET_OK != 939 TALER_JSON_contract_hash (gorc->contract_terms_json, 940 &gorc->h_contract_terms)) ) 941 { 942 GNUNET_break (0); 943 phase_end (gorc, 944 TALER_MHD_reply_with_error (gorc->sc.con, 945 MHD_HTTP_INTERNAL_SERVER_ERROR, 946 TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH, 947 NULL)); 948 return; 949 } 950 GNUNET_assert ( (NULL != gorc->contract_terms_json) || 951 (NULL != gorc->order_json) ); 952 GNUNET_assert ( (NULL != gorc->pc) || 953 (NULL != gorc->order) ); 954 gorc->phase++; 955 } 956 957 958 /** 959 * Check payment status of the order. 960 * 961 * @param[in,out] gorc order context to update 962 */ 963 static void 964 phase_check_paid (struct GetOrderRequestContext *gorc) 965 { 966 struct TMH_HandlerContext *hc = gorc->hc; 967 968 if (gorc->order_only) 969 { 970 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 971 "Order %s unclaimed, no need to lookup payment status\n", 972 hc->infix); 973 GNUNET_assert (! gorc->paid); 974 GNUNET_assert (! gorc->wired); 975 gorc->phase++; 976 return; 977 } 978 if (NULL == gorc->session_id) 979 { 980 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 981 "No session ID, do not need to lookup session-ID specific payment status (%s/%s)\n", 982 gorc->paid ? "paid" : "unpaid", 983 gorc->wired ? "wired" : "unwired"); 984 gorc->phase++; 985 return; 986 } 987 if (! gorc->paid_session_matches) 988 { 989 gorc->paid = false; 990 gorc->wired = false; 991 } 992 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 993 "Order %s %s for session %s (%s)\n", 994 hc->infix, 995 gorc->paid ? "paid" : "unpaid", 996 gorc->session_id, 997 gorc->wired ? "wired" : "unwired"); 998 gorc->phase++; 999 } 1000 1001 1002 /** 1003 * Check if the @a reply satisfies the long-poll not_etag 1004 * constraint. If so, return it as a response for @a gorc, 1005 * otherwise suspend and wait for a change. 1006 * 1007 * @param[in,out] gorc request to handle 1008 * @param reply body for JSON response (#MHD_HTTP_OK) 1009 */ 1010 static void 1011 check_reply (struct GetOrderRequestContext *gorc, 1012 const json_t *reply) 1013 { 1014 struct GNUNET_ShortHashCode sh; 1015 unsigned int http_response_code; 1016 bool not_modified; 1017 struct MHD_Response *response; 1018 char *can; 1019 1020 can = TALER_JSON_canonicalize (reply); 1021 GNUNET_assert (GNUNET_YES == 1022 GNUNET_CRYPTO_hkdf_gnunet (&sh, 1023 sizeof (sh), 1024 "GOR-SALT", 1025 strlen ("GOR-SALT"), 1026 can, 1027 strlen (can))); 1028 not_modified = gorc->have_lp_not_etag && 1029 (0 == GNUNET_memcmp (&sh, 1030 &gorc->lp_not_etag)); 1031 1032 if (not_modified && 1033 (! GNUNET_TIME_absolute_is_past (gorc->sc.long_poll_timeout)) ) 1034 { 1035 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1036 "Status unchanged, not returning response yet\n"); 1037 GNUNET_assert (GNUNET_NO == gorc->suspended); 1038 if (gorc->instant_retry) 1039 { 1040 gorc->instant_retry = false; 1041 gorc->phase = GOP_FETCH_CONTRACT; 1042 GNUNET_free (can); 1043 return; 1044 } 1045 /* note: not necessarily actually unpaid ... */ 1046 GNUNET_CONTAINER_DLL_insert (gorc_head, 1047 gorc_tail, 1048 gorc); 1049 gorc->phase = GOP_SUSPENDED_ON_UNPAID; 1050 gorc->suspended = GNUNET_YES; 1051 MHD_suspend_connection (gorc->sc.con); 1052 GNUNET_free (can); 1053 return; 1054 } 1055 { 1056 const char *inm; 1057 1058 inm = MHD_lookup_connection_value (gorc->sc.con, 1059 MHD_HEADER_KIND, 1060 MHD_HTTP_HEADER_IF_NONE_MATCH); 1061 if ( (NULL == inm) || 1062 ('"' != inm[0]) || 1063 ('"' != inm[strlen (inm) - 1]) || 1064 (0 != strncmp (inm + 1, 1065 can, 1066 strlen (can))) ) 1067 not_modified = false; /* must return full response */ 1068 } 1069 GNUNET_free (can); 1070 http_response_code = not_modified 1071 ? MHD_HTTP_NOT_MODIFIED 1072 : MHD_HTTP_OK; 1073 response = TALER_MHD_make_json (reply); 1074 { 1075 char *etag; 1076 char *qetag; 1077 1078 etag = GNUNET_STRINGS_data_to_string_alloc (&sh, 1079 sizeof (sh)); 1080 GNUNET_asprintf (&qetag, 1081 "\"%s\"", 1082 etag); 1083 GNUNET_break (MHD_YES == 1084 MHD_add_response_header (response, 1085 MHD_HTTP_HEADER_ETAG, 1086 qetag)); 1087 GNUNET_free (qetag); 1088 GNUNET_free (etag); 1089 } 1090 1091 { 1092 enum MHD_Result ret; 1093 1094 ret = MHD_queue_response (gorc->sc.con, 1095 http_response_code, 1096 response); 1097 MHD_destroy_response (response); 1098 phase_end (gorc, 1099 ret); 1100 } 1101 } 1102 1103 1104 /** 1105 * Check if re-purchase detection applies to the order. 1106 * 1107 * @param[in,out] gorc order context to update 1108 */ 1109 static void 1110 phase_check_repurchase (struct GetOrderRequestContext *gorc) 1111 { 1112 struct TMH_HandlerContext *hc = gorc->hc; 1113 char *already_paid_order_id = NULL; 1114 enum GNUNET_DB_QueryStatus qs; 1115 char *taler_pay_uri; 1116 char *order_status_url; 1117 json_t *reply; 1118 1119 if ( (gorc->paid) || 1120 (NULL == gorc->ct->fulfillment_url) || 1121 (NULL == gorc->session_id) ) 1122 { 1123 /* Repurchase cannot apply */ 1124 gorc->phase++; 1125 return; 1126 } 1127 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1128 "Running re-purchase detection for %s/%s\n", 1129 gorc->session_id, 1130 gorc->ct->fulfillment_url); 1131 qs = TALER_MERCHANTDB_get_order_by_fulfillment ( 1132 TMH_db, 1133 hc->instance->settings.id, 1134 gorc->ct->fulfillment_url, 1135 gorc->session_id, 1136 TALER_EXCHANGE_YNA_NO != 1137 gorc->allow_refunded_for_repurchase, 1138 &already_paid_order_id); 1139 if (0 > qs) 1140 { 1141 /* single, read-only SQL statements should never cause 1142 serialization problems, and the entry should exist as per above */ 1143 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); 1144 phase_end (gorc, 1145 TALER_MHD_reply_with_error (gorc->sc.con, 1146 MHD_HTTP_INTERNAL_SERVER_ERROR, 1147 TALER_EC_GENERIC_DB_FETCH_FAILED, 1148 "order by fulfillment")); 1149 return; 1150 } 1151 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 1152 { 1153 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1154 "No already paid order for %s/%s\n", 1155 gorc->session_id, 1156 gorc->ct->fulfillment_url); 1157 gorc->phase++; 1158 return; 1159 } 1160 1161 /* User did pay for this order, but under a different session; ask wallet to 1162 switch order ID */ 1163 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1164 "Found already paid order %s\n", 1165 already_paid_order_id); 1166 taler_pay_uri = TMH_make_taler_pay_uri (gorc->sc.con, 1167 hc->infix, 1168 gorc->session_id, 1169 hc->instance->settings.id, 1170 &gorc->claim_token); 1171 order_status_url = TMH_make_order_status_url (gorc->sc.con, 1172 hc->infix, 1173 gorc->session_id, 1174 hc->instance->settings.id, 1175 &gorc->claim_token, 1176 NULL); 1177 if ( (NULL == taler_pay_uri) || 1178 (NULL == order_status_url) ) 1179 { 1180 GNUNET_break_op (0); 1181 GNUNET_free (taler_pay_uri); 1182 GNUNET_free (order_status_url); 1183 phase_end (gorc, 1184 TALER_MHD_reply_with_error (gorc->sc.con, 1185 MHD_HTTP_BAD_REQUEST, 1186 TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED, 1187 "host")); 1188 return; 1189 } 1190 reply = GNUNET_JSON_PACK ( 1191 GNUNET_JSON_pack_string ("taler_pay_uri", 1192 taler_pay_uri), 1193 GNUNET_JSON_pack_string ("order_status_url", 1194 order_status_url), 1195 GNUNET_JSON_pack_string ("order_status", 1196 "unpaid"), 1197 GNUNET_JSON_pack_string ("already_paid_order_id", 1198 already_paid_order_id), 1199 GNUNET_JSON_pack_string ("already_paid_fulfillment_url", 1200 gorc->ct->fulfillment_url), 1201 /* undefined for unpaid v1 contracts */ 1202 GNUNET_JSON_pack_allow_null ( 1203 TALER_JSON_pack_amount ("total_amount", 1204 TALER_amount_is_valid (&gorc->contract_amount) 1205 ? &gorc->contract_amount 1206 : NULL)), 1207 GNUNET_JSON_pack_object_incref ("proto_contract_terms", 1208 gorc->contract_terms_json), 1209 GNUNET_JSON_pack_string ("summary", 1210 gorc->ct->summary), 1211 GNUNET_JSON_pack_timestamp ("pay_deadline", 1212 NULL != gorc->pc 1213 ? gorc->pc->pay_deadline 1214 : gorc->order->pay_deadline), 1215 GNUNET_JSON_pack_timestamp ("creation_time", 1216 gorc->timestamp)); 1217 1218 GNUNET_free (order_status_url); 1219 GNUNET_free (taler_pay_uri); 1220 GNUNET_free (already_paid_order_id); 1221 check_reply (gorc, 1222 reply); 1223 json_decref (reply); 1224 } 1225 1226 1227 /** 1228 * Check if we should suspend until the order is paid. 1229 * 1230 * @param[in,out] gorc order context to update 1231 */ 1232 static void 1233 phase_unpaid_finish (struct GetOrderRequestContext *gorc) 1234 { 1235 struct TMH_HandlerContext *hc = gorc->hc; 1236 char *order_status_url; 1237 1238 if (gorc->paid) 1239 { 1240 gorc->phase++; 1241 return; 1242 } 1243 /* User never paid for this order, suspend waiting 1244 on payment or return details. */ 1245 if (GNUNET_TIME_absolute_is_future (gorc->sc.long_poll_timeout) && 1246 (! gorc->have_lp_not_etag) ) 1247 { 1248 if (gorc->instant_retry) 1249 { 1250 gorc->instant_retry = false; 1251 gorc->phase = GOP_FETCH_CONTRACT; 1252 return; 1253 } 1254 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1255 "Suspending GET /private/orders/%s\n", 1256 hc->infix); 1257 GNUNET_CONTAINER_DLL_insert (gorc_head, 1258 gorc_tail, 1259 gorc); 1260 gorc->phase = GOP_SUSPENDED_ON_UNPAID; 1261 gorc->suspended = GNUNET_YES; 1262 MHD_suspend_connection (gorc->sc.con); 1263 return; 1264 } 1265 order_status_url = TMH_make_order_status_url (gorc->sc.con, 1266 hc->infix, 1267 gorc->session_id, 1268 hc->instance->settings.id, 1269 &gorc->claim_token, 1270 NULL); 1271 if (! gorc->order_only) 1272 { 1273 json_t *reply; 1274 1275 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1276 "Order %s claimed but not paid yet\n", 1277 hc->infix); 1278 reply = GNUNET_JSON_PACK ( 1279 GNUNET_JSON_pack_string ("order_status_url", 1280 order_status_url), 1281 GNUNET_JSON_pack_object_incref ("contract_terms", 1282 gorc->contract_terms_json), 1283 GNUNET_JSON_pack_string ("order_status", 1284 "claimed")); 1285 GNUNET_free (order_status_url); 1286 check_reply (gorc, 1287 reply); 1288 json_decref (reply); 1289 return; 1290 } 1291 { 1292 char *taler_pay_uri; 1293 json_t *reply; 1294 1295 taler_pay_uri = TMH_make_taler_pay_uri (gorc->sc.con, 1296 hc->infix, 1297 gorc->session_id, 1298 hc->instance->settings.id, 1299 &gorc->claim_token); 1300 reply = GNUNET_JSON_PACK ( 1301 GNUNET_JSON_pack_string ("taler_pay_uri", 1302 taler_pay_uri), 1303 GNUNET_JSON_pack_string ("order_status_url", 1304 order_status_url), 1305 GNUNET_JSON_pack_string ("order_status", 1306 "unpaid"), 1307 GNUNET_JSON_pack_object_incref ("proto_contract_terms", 1308 gorc->contract_terms_json), 1309 /* undefined for unpaid v1 contracts */ 1310 GNUNET_JSON_pack_allow_null ( 1311 TALER_JSON_pack_amount ("total_amount", 1312 &gorc->contract_amount)), 1313 GNUNET_JSON_pack_string ("summary", 1314 gorc->ct->summary), 1315 GNUNET_JSON_pack_timestamp ("creation_time", 1316 gorc->timestamp)); 1317 check_reply (gorc, 1318 reply); 1319 json_decref (reply); 1320 GNUNET_free (taler_pay_uri); 1321 } 1322 GNUNET_free (order_status_url); 1323 } 1324 1325 1326 /** 1327 * Function called with each @a coin_pub that was deposited into the 1328 * @a h_wire account of the merchant for the @a deposit_serial as part 1329 * of the payment for the order identified by @a cls. 1330 * 1331 * Queries the exchange for the payment status associated with the 1332 * given coin. 1333 * 1334 * @param cls a `struct GetOrderRequestContext` 1335 * @param deposit_serial identifies the deposit operation 1336 * @param exchange_url URL of the exchange that issued @a coin_pub 1337 * @param h_wire hash of the merchant's wire account into which the deposit was made 1338 * @param deposit_timestamp when was the deposit made 1339 * @param amount_with_fee amount the exchange will deposit for this coin 1340 * @param deposit_fee fee the exchange will charge for this coin 1341 * @param coin_pub public key of the deposited coin 1342 */ 1343 static void 1344 deposit_cb ( 1345 void *cls, 1346 uint64_t deposit_serial, 1347 const char *exchange_url, 1348 const struct TALER_MerchantWireHashP *h_wire, 1349 struct GNUNET_TIME_Timestamp deposit_timestamp, 1350 const struct TALER_Amount *amount_with_fee, 1351 const struct TALER_Amount *deposit_fee, 1352 const struct TALER_CoinSpendPublicKeyP *coin_pub) 1353 { 1354 struct GetOrderRequestContext *gorc = cls; 1355 struct TransferQuery *tq; 1356 1357 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1358 "Checking deposit status for coin %s (over %s)\n", 1359 TALER_B2S (coin_pub), 1360 TALER_amount2s (amount_with_fee)); 1361 gorc->last_payment 1362 = GNUNET_TIME_timestamp_max (gorc->last_payment, 1363 deposit_timestamp); 1364 tq = GNUNET_new (struct TransferQuery); 1365 tq->gorc = gorc; 1366 tq->exchange_url = GNUNET_strdup (exchange_url); 1367 tq->deposit_serial = deposit_serial; 1368 GNUNET_CONTAINER_DLL_insert (gorc->tq_head, 1369 gorc->tq_tail, 1370 tq); 1371 tq->coin_pub = *coin_pub; 1372 tq->h_wire = *h_wire; 1373 tq->amount_with_fee = *amount_with_fee; 1374 tq->deposit_fee = *deposit_fee; 1375 } 1376 1377 1378 /** 1379 * Check wire transfer status for the order at the exchange. 1380 * 1381 * @param[in,out] gorc order context to update 1382 */ 1383 static void 1384 phase_check_deposits (struct GetOrderRequestContext *gorc) 1385 { 1386 GNUNET_assert (! gorc->order_only); 1387 GNUNET_assert (gorc->paid); 1388 1389 /* amount must be always valid for paid orders */ 1390 GNUNET_assert (GNUNET_OK == 1391 TALER_amount_is_valid (&gorc->contract_amount)); 1392 1393 GNUNET_assert (GNUNET_OK == 1394 TALER_amount_set_zero (gorc->contract_amount.currency, 1395 &gorc->deposits_total)); 1396 GNUNET_assert (GNUNET_OK == 1397 TALER_amount_set_zero (gorc->contract_amount.currency, 1398 &gorc->deposit_fees_total)); 1399 GNUNET_assert (GNUNET_OK == 1400 TALER_amount_set_zero (gorc->contract_amount.currency, 1401 &gorc->deposit_fees_refunded_total)); 1402 TALER_MERCHANTDB_iterate_deposits_by_order (TMH_db, 1403 gorc->order_serial, 1404 &deposit_cb, 1405 gorc); 1406 gorc->phase++; 1407 } 1408 1409 1410 /** 1411 * Function called with information about a refund. 1412 * It is responsible for summing up the refund amount. 1413 * 1414 * @param cls closure 1415 * @param refund_serial unique serial number of the refund 1416 * @param timestamp time of the refund (for grouping of refunds in the wallet UI) 1417 * @param coin_pub public coin from which the refund comes from 1418 * @param exchange_url URL of the exchange that issued @a coin_pub 1419 * @param rtransaction_id identificator of the refund 1420 * @param reason human-readable explanation of the refund 1421 * @param refund_amount refund amount which is being taken from @a coin_pub 1422 * @param pending true if the this refund was not yet processed by the wallet/exchange 1423 */ 1424 static void 1425 process_refunds_cb ( 1426 void *cls, 1427 uint64_t refund_serial, 1428 struct GNUNET_TIME_Timestamp timestamp, 1429 const struct TALER_CoinSpendPublicKeyP *coin_pub, 1430 const char *exchange_url, 1431 uint64_t rtransaction_id, 1432 const char *reason, 1433 const struct TALER_Amount *refund_amount, 1434 bool pending) 1435 { 1436 struct GetOrderRequestContext *gorc = cls; 1437 1438 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1439 "Found refund %llu over %s for reason %s\n", 1440 (unsigned long long) rtransaction_id, 1441 TALER_amount2s (refund_amount), 1442 reason); 1443 GNUNET_assert ( 1444 0 == 1445 json_array_append_new ( 1446 gorc->refund_details, 1447 GNUNET_JSON_PACK ( 1448 TALER_JSON_pack_amount ("amount", 1449 refund_amount), 1450 GNUNET_JSON_pack_bool ("pending", 1451 pending), 1452 GNUNET_JSON_pack_timestamp ("timestamp", 1453 timestamp), 1454 GNUNET_JSON_pack_string ("reason", 1455 reason)))); 1456 /* For refunded coins, we are not charged deposit fees, so subtract those 1457 again */ 1458 for (struct TransferQuery *tq = gorc->tq_head; 1459 NULL != tq; 1460 tq = tq->next) 1461 { 1462 if (0 != 1463 strcmp (exchange_url, 1464 tq->exchange_url)) 1465 continue; 1466 if (0 != 1467 GNUNET_memcmp (&tq->coin_pub, 1468 coin_pub)) 1469 continue; 1470 if (GNUNET_OK != 1471 TALER_amount_cmp_currency ( 1472 &gorc->deposit_fees_total, 1473 &tq->deposit_fee)) 1474 { 1475 gorc->refund_currency_mismatch = true; 1476 return; 1477 } 1478 GNUNET_assert ( 1479 0 <= 1480 TALER_amount_add (&gorc->deposit_fees_refunded_total, 1481 &gorc->deposit_fees_refunded_total, 1482 &tq->deposit_fee)); 1483 } 1484 if (GNUNET_OK != 1485 TALER_amount_cmp_currency ( 1486 &gorc->refund_amount, 1487 refund_amount)) 1488 { 1489 gorc->refund_currency_mismatch = true; 1490 return; 1491 } 1492 GNUNET_assert (0 <= 1493 TALER_amount_add (&gorc->refund_amount, 1494 &gorc->refund_amount, 1495 refund_amount)); 1496 gorc->refunded = true; 1497 gorc->refund_pending |= pending; 1498 } 1499 1500 1501 /** 1502 * Function called with information about an external refund. 1503 * Appends the refund entry to the "refunds_external" array. 1504 * 1505 * @param cls a `struct GetOrderRequestContext` 1506 * @param refund_id identifier of the refund within the order 1507 * @param refund_timestamp when was the refund recorded 1508 * @param method external payment method used for the refund 1509 * @param payment_id id of the original external payment entry 1510 * @param amount amount returned to the customer 1511 * @param reason human-readable refund justification 1512 */ 1513 static void 1514 process_external_refunds_cb (void *cls, 1515 const char *refund_id, 1516 struct GNUNET_TIME_Timestamp refund_timestamp, 1517 const char *method, 1518 const char *payment_id, 1519 const struct TALER_Amount *amount, 1520 const char *reason) 1521 { 1522 struct GetOrderRequestContext *gorc = cls; 1523 1524 GNUNET_assert ( 1525 0 == 1526 json_array_append_new ( 1527 gorc->refunds_external, 1528 GNUNET_JSON_PACK ( 1529 GNUNET_JSON_pack_string ("id", 1530 refund_id), 1531 GNUNET_JSON_pack_string ("method", 1532 method), 1533 GNUNET_JSON_pack_allow_null ( 1534 GNUNET_JSON_pack_string ("payment_id", 1535 payment_id)), 1536 TALER_JSON_pack_amount ("amount", 1537 amount), 1538 GNUNET_JSON_pack_string ("reason", 1539 reason), 1540 GNUNET_JSON_pack_timestamp ("timestamp", 1541 refund_timestamp)))); 1542 } 1543 1544 1545 /** 1546 * Check refund status for the order. 1547 * 1548 * @param[in,out] gorc order context to update 1549 */ 1550 static void 1551 phase_check_refunds (struct GetOrderRequestContext *gorc) 1552 { 1553 struct TMH_HandlerContext *hc = gorc->hc; 1554 enum GNUNET_DB_QueryStatus qs; 1555 1556 GNUNET_assert (! gorc->order_only); 1557 GNUNET_assert (gorc->paid); 1558 1559 /* Accumulate refunds, if any. */ 1560 GNUNET_assert (GNUNET_OK == 1561 TALER_amount_set_zero (gorc->contract_amount.currency, 1562 &gorc->refund_amount)); 1563 json_array_clear (gorc->refund_details); 1564 qs = TALER_MERCHANTDB_iterate_refunds_detailed ( 1565 TMH_db, 1566 hc->instance->settings.id, 1567 &gorc->h_contract_terms, 1568 &process_refunds_cb, 1569 gorc); 1570 if (0 > qs) 1571 { 1572 GNUNET_break (0); 1573 phase_end (gorc, 1574 TALER_MHD_reply_with_error ( 1575 gorc->sc.con, 1576 MHD_HTTP_INTERNAL_SERVER_ERROR, 1577 TALER_EC_GENERIC_DB_FETCH_FAILED, 1578 "detailed refunds")); 1579 return; 1580 } 1581 if (gorc->refund_currency_mismatch) 1582 { 1583 GNUNET_break (0); 1584 phase_end (gorc, 1585 TALER_MHD_reply_with_error ( 1586 gorc->sc.con, 1587 MHD_HTTP_INTERNAL_SERVER_ERROR, 1588 TALER_EC_GENERIC_DB_FETCH_FAILED, 1589 "refunds in different currency than original order price")); 1590 return; 1591 } 1592 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1593 "Total refunds are %s\n", 1594 TALER_amount2s (&gorc->refund_amount)); 1595 json_array_clear (gorc->refunds_external); 1596 qs = TALER_MERCHANTDB_iterate_external_refunds ( 1597 TMH_db, 1598 hc->instance->settings.id, 1599 hc->infix, 1600 &process_external_refunds_cb, 1601 gorc); 1602 if (0 > qs) 1603 { 1604 GNUNET_break (0); 1605 phase_end (gorc, 1606 TALER_MHD_reply_with_error ( 1607 gorc->sc.con, 1608 MHD_HTTP_INTERNAL_SERVER_ERROR, 1609 TALER_EC_GENERIC_DB_FETCH_FAILED, 1610 "external refunds")); 1611 return; 1612 } 1613 gorc->phase++; 1614 } 1615 1616 1617 /** 1618 * Function called with available wire details, to be added to 1619 * the response. 1620 * 1621 * @param cls a `struct GetOrderRequestContext` 1622 * @param wtid wire transfer subject of the wire transfer for the coin 1623 * @param exchange_url base URL of the exchange that made the payment 1624 * @param execution_time when was the payment made 1625 * @param deposit_value contribution of the coin to the total wire transfer value 1626 * @param deposit_fee deposit fee charged by the exchange for the coin 1627 * @param transfer_confirmed did the merchant confirm that a wire transfer with 1628 * @a wtid over the total amount happened? 1629 * @param expected_credit_serial row for the expected wire transfer this 1630 * entry references 1631 */ 1632 static void 1633 process_transfer_details ( 1634 void *cls, 1635 const struct TALER_WireTransferIdentifierRawP *wtid, 1636 const char *exchange_url, 1637 struct GNUNET_TIME_Timestamp execution_time, 1638 const struct TALER_Amount *deposit_value, 1639 const struct TALER_Amount *deposit_fee, 1640 bool transfer_confirmed, 1641 uint64_t expected_credit_serial) 1642 { 1643 struct GetOrderRequestContext *gorc = cls; 1644 json_t *wire_details = gorc->wire_details; 1645 struct TALER_Amount wired; 1646 1647 if ( (GNUNET_OK != 1648 TALER_amount_cmp_currency (&gorc->deposits_total, 1649 deposit_value)) || 1650 (GNUNET_OK != 1651 TALER_amount_cmp_currency (&gorc->deposit_fees_total, 1652 deposit_fee)) ) 1653 { 1654 GNUNET_break (0); 1655 gorc->deposit_currency_mismatch = true; 1656 return; 1657 } 1658 1659 /* Compute total amount *wired* */ 1660 GNUNET_assert (0 <= 1661 TALER_amount_add (&gorc->deposits_total, 1662 &gorc->deposits_total, 1663 deposit_value)); 1664 GNUNET_assert (0 <= 1665 TALER_amount_add (&gorc->deposit_fees_total, 1666 &gorc->deposit_fees_total, 1667 deposit_fee)); 1668 GNUNET_assert (0 <= TALER_amount_subtract (&wired, 1669 deposit_value, 1670 deposit_fee)); 1671 GNUNET_assert (0 == 1672 json_array_append_new ( 1673 wire_details, 1674 GNUNET_JSON_PACK ( 1675 GNUNET_JSON_pack_data_auto ("wtid", 1676 wtid), 1677 GNUNET_JSON_pack_string ("exchange_url", 1678 exchange_url), 1679 TALER_JSON_pack_amount ("amount", 1680 &wired), 1681 TALER_JSON_pack_amount ("deposit_fee", 1682 deposit_fee), 1683 GNUNET_JSON_pack_timestamp ("execution_time", 1684 execution_time), 1685 GNUNET_JSON_pack_bool ("confirmed", 1686 transfer_confirmed), 1687 GNUNET_JSON_pack_uint64 ("expected_transfer_serial_id", 1688 expected_credit_serial)))); 1689 } 1690 1691 1692 /** 1693 * Check transfer status in local database. 1694 * 1695 * @param[in,out] gorc order context to update 1696 */ 1697 static void 1698 phase_check_local_transfers (struct GetOrderRequestContext *gorc) 1699 { 1700 struct TMH_HandlerContext *hc = gorc->hc; 1701 enum GNUNET_DB_QueryStatus qs; 1702 1703 GNUNET_assert (gorc->paid); 1704 GNUNET_assert (! gorc->order_only); 1705 1706 GNUNET_assert (GNUNET_OK == 1707 TALER_amount_set_zero (gorc->contract_amount.currency, 1708 &gorc->deposits_total)); 1709 GNUNET_assert (GNUNET_OK == 1710 TALER_amount_set_zero (gorc->contract_amount.currency, 1711 &gorc->deposit_fees_total)); 1712 GNUNET_assert (NULL != gorc->wire_details); 1713 /* We may be running again due to long-polling, clear state first */ 1714 json_array_clear (gorc->wire_details); 1715 qs = TALER_MERCHANTDB_iterate_transfer_details_by_order (TMH_db, 1716 gorc->order_serial, 1717 &process_transfer_details, 1718 gorc); 1719 if (0 > qs) 1720 { 1721 GNUNET_break (0); 1722 phase_end (gorc, 1723 TALER_MHD_reply_with_error (gorc->sc.con, 1724 MHD_HTTP_INTERNAL_SERVER_ERROR, 1725 TALER_EC_GENERIC_DB_FETCH_FAILED, 1726 "transfer details")); 1727 return; 1728 } 1729 if (gorc->deposit_currency_mismatch) 1730 { 1731 GNUNET_break (0); 1732 phase_end (gorc, 1733 TALER_MHD_reply_with_error (gorc->sc.con, 1734 MHD_HTTP_INTERNAL_SERVER_ERROR, 1735 TALER_EC_GENERIC_DB_FETCH_FAILED, 1736 "deposits in different currency than original order price")); 1737 return; 1738 } 1739 1740 if (! gorc->wired) 1741 { 1742 /* we believe(d) the wire transfer did not happen yet, check if maybe 1743 in light of new evidence it did */ 1744 struct TALER_Amount expect_total; 1745 1746 if (0 > 1747 TALER_amount_subtract (&expect_total, 1748 &gorc->contract_amount, 1749 &gorc->refund_amount)) 1750 { 1751 GNUNET_break (0); 1752 phase_end (gorc, 1753 TALER_MHD_reply_with_error ( 1754 gorc->sc.con, 1755 MHD_HTTP_INTERNAL_SERVER_ERROR, 1756 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 1757 "refund exceeds contract value")); 1758 return; 1759 } 1760 GNUNET_assert ( 1761 0 <= 1762 TALER_amount_add (&expect_total, 1763 &expect_total, 1764 &gorc->deposit_fees_refunded_total)); 1765 1766 if (0 > 1767 TALER_amount_subtract (&expect_total, 1768 &expect_total, 1769 &gorc->deposit_fees_total)) 1770 { 1771 GNUNET_break (0); 1772 phase_end (gorc, 1773 TALER_MHD_reply_with_error ( 1774 gorc->sc.con, 1775 MHD_HTTP_INTERNAL_SERVER_ERROR, 1776 TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, 1777 "deposit fees exceed total minus refunds")); 1778 return; 1779 } 1780 if (0 >= 1781 TALER_amount_cmp (&expect_total, 1782 &gorc->deposits_total)) 1783 { 1784 /* expect_total <= gorc->deposits_total: good: we got the wire transfer */ 1785 gorc->wired = true; 1786 qs = TALER_MERCHANTDB_update_to_contract_terms_wired (TMH_db, 1787 gorc->order_serial); 1788 GNUNET_break (qs >= 0); /* just warn if transaction failed */ 1789 TMH_notify_order_change (hc->instance, 1790 TMH_OSF_PAID 1791 | TMH_OSF_WIRED, 1792 gorc->timestamp, 1793 gorc->order_serial); 1794 } 1795 } 1796 gorc->phase++; 1797 } 1798 1799 1800 /** 1801 * Generate final result for the status request. 1802 * 1803 * @param[in,out] gorc order context to update 1804 */ 1805 static void 1806 phase_reply_result (struct GetOrderRequestContext *gorc) 1807 { 1808 struct TMH_HandlerContext *hc = gorc->hc; 1809 char *order_status_url; 1810 1811 GNUNET_assert (gorc->paid); 1812 GNUNET_assert (! gorc->order_only); 1813 1814 { 1815 struct TALER_PrivateContractHashP *h_contract = NULL; 1816 1817 /* In a session-bound payment, allow the browser to check the order 1818 * status page (e.g. to get a refund). 1819 * 1820 * Note that we don't allow this outside of session-based payment, as 1821 * otherwise this becomes an oracle to convert order_id to h_contract. 1822 */ 1823 if (NULL != gorc->session_id) 1824 h_contract = &gorc->h_contract_terms; 1825 1826 order_status_url = 1827 TMH_make_order_status_url (gorc->sc.con, 1828 hc->infix, 1829 gorc->session_id, 1830 hc->instance->settings.id, 1831 &gorc->claim_token, 1832 h_contract); 1833 } 1834 if (GNUNET_TIME_absolute_is_zero (gorc->last_payment.abs_time)) 1835 { 1836 GNUNET_break (GNUNET_YES == 1837 TALER_amount_is_zero (&gorc->contract_amount)); 1838 gorc->last_payment = gorc->timestamp; 1839 } 1840 { 1841 json_t *reply; 1842 1843 reply = GNUNET_JSON_PACK ( 1844 // Deprecated in protocol v6! 1845 GNUNET_JSON_pack_array_steal ("wire_reports", 1846 json_array ()), 1847 GNUNET_JSON_pack_uint64 ("exchange_code", 1848 gorc->exchange_ec), 1849 GNUNET_JSON_pack_uint64 ("exchange_http_status", 1850 gorc->exchange_hc), 1851 /* legacy: */ 1852 GNUNET_JSON_pack_uint64 ("exchange_ec", 1853 gorc->exchange_ec), 1854 /* legacy: */ 1855 GNUNET_JSON_pack_uint64 ("exchange_hc", 1856 gorc->exchange_hc), 1857 TALER_JSON_pack_amount ("deposit_total", 1858 &gorc->deposits_total), 1859 GNUNET_JSON_pack_object_incref ("contract_terms", 1860 gorc->contract_terms_json), 1861 GNUNET_JSON_pack_string ("order_status", 1862 "paid"), 1863 GNUNET_JSON_pack_timestamp ("last_payment", 1864 gorc->last_payment), 1865 GNUNET_JSON_pack_bool ("refunded", 1866 gorc->refunded), 1867 GNUNET_JSON_pack_bool ("wired", 1868 gorc->wired), 1869 GNUNET_JSON_pack_bool ("refund_pending", 1870 gorc->refund_pending), 1871 GNUNET_JSON_pack_allow_null ( 1872 TALER_JSON_pack_amount ("refund_amount", 1873 &gorc->refund_amount)), 1874 GNUNET_JSON_pack_array_incref ("wire_details", 1875 gorc->wire_details), 1876 GNUNET_JSON_pack_array_incref ("refund_details", 1877 gorc->refund_details), 1878 GNUNET_JSON_pack_array_incref ("refunds_external", 1879 gorc->refunds_external), 1880 GNUNET_JSON_pack_string ("order_status_url", 1881 order_status_url), 1882 (gorc->choice_index >= 0) 1883 ? GNUNET_JSON_pack_int64 ("choice_index", 1884 gorc->choice_index) 1885 : GNUNET_JSON_pack_end_ ()); 1886 check_reply (gorc, 1887 reply); 1888 json_decref (reply); 1889 } 1890 GNUNET_free (order_status_url); 1891 } 1892 1893 1894 /** 1895 * End with error status in wire_hc and wire_ec. 1896 * 1897 * @param[in,out] gorc order context to update 1898 */ 1899 static void 1900 phase_error (struct GetOrderRequestContext *gorc) 1901 { 1902 GNUNET_assert (TALER_EC_NONE != gorc->wire_ec); 1903 phase_end (gorc, 1904 TALER_MHD_reply_with_error (gorc->sc.con, 1905 gorc->wire_hc, 1906 gorc->wire_ec, 1907 NULL)); 1908 } 1909 1910 1911 enum MHD_Result 1912 TMH_private_get_orders_ID ( 1913 const struct TMH_RequestHandler *rh, 1914 struct MHD_Connection *connection, 1915 struct TMH_HandlerContext *hc) 1916 { 1917 struct GetOrderRequestContext *gorc = hc->ctx; 1918 1919 if (NULL == gorc) 1920 { 1921 /* First time here, parse request and check order is known */ 1922 GNUNET_assert (NULL != hc->infix); 1923 gorc = GNUNET_new (struct GetOrderRequestContext); 1924 hc->cc = &gorc_cleanup; 1925 hc->ctx = gorc; 1926 gorc->sc.con = connection; 1927 gorc->hc = hc; 1928 gorc->wire_details = json_array (); 1929 GNUNET_assert (NULL != gorc->wire_details); 1930 gorc->refund_details = json_array (); 1931 GNUNET_assert (NULL != gorc->refund_details); 1932 gorc->refunds_external = json_array (); 1933 GNUNET_assert (NULL != gorc->refunds_external); 1934 gorc->session_id = MHD_lookup_connection_value (connection, 1935 MHD_GET_ARGUMENT_KIND, 1936 "session_id"); 1937 if (! (TALER_MHD_arg_to_yna (connection, 1938 "allow_refunded_for_repurchase", 1939 TALER_EXCHANGE_YNA_NO, 1940 &gorc->allow_refunded_for_repurchase)) ) 1941 return TALER_MHD_reply_with_error (connection, 1942 MHD_HTTP_BAD_REQUEST, 1943 TALER_EC_GENERIC_PARAMETER_MALFORMED, 1944 "allow_refunded_for_repurchase"); 1945 TALER_MHD_parse_request_timeout (connection, 1946 &gorc->sc.long_poll_timeout); 1947 TALER_MHD_parse_request_arg_auto (connection, 1948 "lp_not_etag", 1949 &gorc->lp_not_etag, 1950 gorc->have_lp_not_etag); 1951 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1952 "Starting GET /private/orders/%s processing with timeout %s\n", 1953 hc->infix, 1954 GNUNET_STRINGS_absolute_time_to_string ( 1955 gorc->sc.long_poll_timeout)); 1956 } 1957 if (GNUNET_SYSERR == gorc->suspended) 1958 return MHD_NO; /* we are in shutdown */ 1959 while (1) 1960 { 1961 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1962 "Processing order %s in phase %d\n", 1963 hc->infix, 1964 (int) gorc->phase); 1965 switch (gorc->phase) 1966 { 1967 case GOP_INIT: 1968 phase_init (gorc); 1969 break; 1970 case GOP_FETCH_CONTRACT: 1971 phase_fetch_contract (gorc); 1972 break; 1973 case GOP_PARSE_CONTRACT: 1974 phase_parse_contract (gorc); 1975 break; 1976 case GOP_CHECK_PAID: 1977 phase_check_paid (gorc); 1978 break; 1979 case GOP_CHECK_REPURCHASE: 1980 phase_check_repurchase (gorc); 1981 break; 1982 case GOP_UNPAID_FINISH: 1983 phase_unpaid_finish (gorc); 1984 break; 1985 case GOP_CHECK_DEPOSITS: 1986 phase_check_deposits (gorc); 1987 break; 1988 case GOP_CHECK_REFUNDS: 1989 phase_check_refunds (gorc); 1990 break; 1991 case GOP_CHECK_LOCAL_TRANSFERS: 1992 phase_check_local_transfers (gorc); 1993 break; 1994 case GOP_REPLY_RESULT: 1995 phase_reply_result (gorc); 1996 break; 1997 case GOP_ERROR: 1998 phase_error (gorc); 1999 break; 2000 case GOP_SUSPENDED_ON_UNPAID: 2001 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2002 "Suspending order request awaiting payment\n"); 2003 return MHD_YES; 2004 case GOP_END_YES: 2005 return MHD_YES; 2006 case GOP_END_NO: 2007 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2008 "Closing connection, no response generated\n"); 2009 return MHD_NO; 2010 } 2011 } /* end first-time per-request initialization */ 2012 }