LCOV - code coverage report
 Current view: top level - backend - taler-merchant-httpd_post-orders-ID-pay.c (source / functions) Hit Total Coverage Test: GNU Taler merchant coverage report Lines: 371 579 64.1 % Date: 2021-08-30 06:54:17 Functions: 15 17 88.2 % Legend: Lines: hit not hit
  Line data Source code  1 : /* 2 : This file is part of TALER 3 : (C) 2014-2021 Taler Systems SA 4 : 5 : TALER is free software; you can redistribute it and/or modify 6 : it under the terms of the GNU Affero General Public License as 7 : published by the Free Software Foundation; either version 3, 8 : or (at your option) any later version. 9 : 10 : TALER is distributed in the hope that it will be useful, but 11 : WITHOUT ANY WARRANTY; without even the implied warranty of 12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 : GNU General Public License for more details. 14 : 15 : You should have received a copy of the GNU General Public 16 : License along with TALER; see the file COPYING. If not, 17 : see 18 : */ 19 : 20 : /** 21 : * @file taler-merchant-httpd_post-orders-ID-pay.c 22 : * @brief handling of POST /orders/$ID/pay requests 23 : * @author Marcello Stanisci 24 : * @author Christian Grothoff 25 : * @author Florian Dold 26 : */ 27 : #include "platform.h" 28 : #include 29 : #include 30 : #include 31 : #include 32 : #include "taler-merchant-httpd_auditors.h" 33 : #include "taler-merchant-httpd_exchanges.h" 34 : #include "taler-merchant-httpd_helper.h" 35 : #include "taler-merchant-httpd_post-orders-ID-pay.h" 36 : #include "taler-merchant-httpd_private-get-orders.h" 37 : 38 : 39 : /** 40 : * How often do we retry the (complex!) database transaction? 41 : */ 42 : #define MAX_RETRIES 5 43 : 44 : /** 45 : * Maximum number of coins that we allow per transaction 46 : */ 47 : #define MAX_COIN_ALLOWED_COINS 1024 48 : 49 : /** 50 : * Information we keep for an individual call to the pay handler. 51 : */ 52 : struct PayContext; 53 : 54 : /** 55 : * Information kept during a pay request for each coin. 56 : */ 57 : struct DepositConfirmation 58 : { 59 : 60 : /** 61 : * Reference to the main PayContext 62 : */ 63 : struct PayContext *pc; 64 : 65 : /** 66 : * Handle to the deposit operation we are performing for 67 : * this coin, NULL after the operation is done. 68 : */ 69 : struct TALER_EXCHANGE_DepositHandle *dh; 70 : 71 : /** 72 : * URL of the exchange that issued this coin. 73 : */ 74 : char *exchange_url; 75 : 76 : /** 77 : * Hash of the denomination of this coin. 78 : */ 79 : struct GNUNET_HashCode h_denom; 80 : 81 : /** 82 : * Amount this coin contributes to the total purchase price. 83 : * This amount includes the deposit fee. 84 : */ 85 : struct TALER_Amount amount_with_fee; 86 : 87 : /** 88 : * Fee charged by the exchange for the deposit operation of this coin. 89 : */ 90 : struct TALER_Amount deposit_fee; 91 : 92 : /** 93 : * Fee charged by the exchange for the refund operation of this coin. 94 : */ 95 : struct TALER_Amount refund_fee; 96 : 97 : /** 98 : * Wire fee charged by the exchange of this coin. 99 : */ 100 : struct TALER_Amount wire_fee; 101 : 102 : /** 103 : * Public key of the coin. 104 : */ 105 : struct TALER_CoinSpendPublicKeyP coin_pub; 106 : 107 : /** 108 : * Signature using the @e denom key over the @e coin_pub. 109 : */ 110 : struct TALER_DenominationSignature ub_sig; 111 : 112 : /** 113 : * Signature of the coin's private key over the contract. 114 : */ 115 : struct TALER_CoinSpendSignatureP coin_sig; 116 : 117 : /** 118 : * Offset of this coin into the dc array of all coins in the 119 : * @e pc. 120 : */ 121 : unsigned int index; 122 : 123 : /** 124 : * true if we found this coin in the database. 125 : */ 126 : bool found_in_db; 127 : 128 : }; 129 : 130 : 131 : /** 132 : * Information we keep for an individual call to the /pay handler. 133 : */ 134 : struct PayContext 135 : { 136 : 137 : /** 138 : * Stored in a DLL. 139 : */ 140 : struct PayContext *next; 141 : 142 : /** 143 : * Stored in a DLL. 144 : */ 145 : struct PayContext *prev; 146 : 147 : /** 148 : * Array with @e coins_cnt coins we are despositing. 149 : */ 150 : struct DepositConfirmation *dc; 151 : 152 : /** 153 : * MHD connection to return to 154 : */ 155 : struct MHD_Connection *connection; 156 : 157 : /** 158 : * Details about the client's request. 159 : */ 160 : struct TMH_HandlerContext *hc; 161 : 162 : /** 163 : * What wire method (of the @e mi) was selected by the wallet? 164 : * Set in #parse_pay(). 165 : */ 166 : struct TMH_WireMethod *wm; 167 : 168 : /** 169 : * Task called when the (suspended) processing for 170 : * the /pay request times out. 171 : * Happens when we don't get a response from the exchange. 172 : */ 173 : struct GNUNET_SCHEDULER_Task *timeout_task; 174 : 175 : /** 176 : * Response to return, NULL if we don't have one yet. 177 : */ 178 : struct MHD_Response *response; 179 : 180 : /** 181 : * Handle for operation to lookup /keys (and auditors) from 182 : * the exchange used for this transaction; NULL if no operation is 183 : * pending. 184 : */ 185 : struct TMH_EXCHANGES_FindOperation *fo; 186 : 187 : /** 188 : * URL of the exchange used for the last @e fo. 189 : */ 190 : const char *current_exchange; 191 : 192 : /** 193 : * Placeholder for #TALER_MHD_parse_post_json() to keep its internal state. 194 : */ 195 : void *json_parse_context; 196 : 197 : /** 198 : * Optional session id given in @e root. 199 : * NULL if not given. 200 : */ 201 : char *session_id; 202 : 203 : /** 204 : * Transaction ID given in @e root. 205 : */ 206 : const char *order_id; 207 : 208 : /** 209 : * Fulfillment URL from the contract, or NULL if we don't have one. 210 : */ 211 : char *fulfillment_url; 212 : 213 : /** 214 : * Serial number of this order in the database (set once we did the lookup). 215 : */ 216 : uint64_t order_serial; 217 : 218 : /** 219 : * Hashed proposal. 220 : */ 221 : struct GNUNET_HashCode h_contract_terms; 222 : 223 : /** 224 : * "h_wire" from @e contract_terms. Used to identify 225 : * the instance's wire transfer method. 226 : */ 227 : struct GNUNET_HashCode h_wire; 228 : 229 : /** 230 : * Maximum fee the merchant is willing to pay, from @e root. 231 : * Note that IF the total fee of the exchange is higher, that is 232 : * acceptable to the merchant if the customer is willing to 233 : * pay the difference 234 : * (i.e. amount - max_fee <= actual-amount - actual-fee). 235 : */ 236 : struct TALER_Amount max_fee; 237 : 238 : /** 239 : * Maximum wire fee the merchant is willing to pay, from @e root. 240 : * Note that IF the total fee of the exchange is higher, that is 241 : * acceptable to the merchant if the customer is willing to 242 : * pay the amorized difference. Wire fees are charged over an 243 : * aggregate of several translations, hence unlike the deposit 244 : * fees, they are amortized over several customer's transactions. 245 : * The contract specifies under @e wire_fee_amortization how many 246 : * customer's transactions he expects the wire fees to be amortized 247 : * over on average. Thus, if the wire fees are larger than 248 : * @e max_wire_fee, each customer is expected to contribute 249 : *$\frac{actual-wire-fee - max_wire_fee}{wire_fee_amortization}$. 250 : * The customer's contribution may be further reduced by the 251 : * difference between @e max_fee and the sum of the deposit fees. 252 : * 253 : * Default is that the merchant is unwilling to pay any wire fees. 254 : */ 255 : struct TALER_Amount max_wire_fee; 256 : 257 : /** 258 : * Amount from @e root. This is the amount the merchant expects 259 : * to make, minus @e max_fee. 260 : */ 261 : struct TALER_Amount amount; 262 : 263 : /** 264 : * Considering all the coins with the "found_in_db" flag 265 : * set, what is the total amount we were so far paid on 266 : * this contract? 267 : */ 268 : struct TALER_Amount total_paid; 269 : 270 : /** 271 : * Considering all the coins with the "found_in_db" flag 272 : * set, what is the total amount we had to pay in deposit 273 : * fees so far on this contract? 274 : */ 275 : struct TALER_Amount total_fees_paid; 276 : 277 : /** 278 : * Considering all the coins with the "found_in_db" flag 279 : * set, what is the total amount we already refunded? 280 : */ 281 : struct TALER_Amount total_refunded; 282 : 283 : /** 284 : * Wire transfer deadline. How soon would the merchant like the 285 : * wire transfer to be executed? 286 : */ 287 : struct GNUNET_TIME_Absolute wire_transfer_deadline; 288 : 289 : /** 290 : * Timestamp from @e contract_terms. 291 : */ 292 : struct GNUNET_TIME_Absolute timestamp; 293 : 294 : /** 295 : * Refund deadline from @e contract_terms. 296 : */ 297 : struct GNUNET_TIME_Absolute refund_deadline; 298 : 299 : /** 300 : * Deadline for the customer to pay for this proposal. 301 : */ 302 : struct GNUNET_TIME_Absolute pay_deadline; 303 : 304 : /** 305 : * Number of transactions that the wire fees are expected to be 306 : * amortized over. Never zero, defaults (conservateively) to 1. 307 : * May be higher if merchants expect many small transactions to 308 : * be aggregated and thus wire fees to be reasonably amortized 309 : * due to aggregation. 310 : */ 311 : uint32_t wire_fee_amortization; 312 : 313 : /** 314 : * Number of coins this payment is made of. Length 315 : * of the @e dc array. 316 : */ 317 : unsigned int coins_cnt; 318 : 319 : /** 320 : * How often have we retried the 'main' transaction? 321 : */ 322 : unsigned int retry_counter; 323 : 324 : /** 325 : * Number of transactions still pending. Initially set to 326 : * @e coins_cnt, decremented on each transaction that 327 : * successfully finished. 328 : */ 329 : unsigned int pending; 330 : 331 : /** 332 : * Number of transactions still pending for the currently selected 333 : * exchange. Initially set to the number of coins started at the 334 : * exchange, decremented on each transaction that successfully 335 : * finished. Once it hits zero, we pick the next exchange. 336 : */ 337 : unsigned int pending_at_ce; 338 : 339 : /** 340 : * HTTP status code to use for the reply, i.e 200 for "OK". 341 : * Special value UINT_MAX is used to indicate hard errors 342 : * (no reply, return #MHD_NO). 343 : */ 344 : unsigned int response_code; 345 : 346 : /** 347 : * #GNUNET_NO if the @e connection was not suspended, 348 : * #GNUNET_YES if the @e connection was suspended, 349 : * #GNUNET_SYSERR if @e connection was resumed to as 350 : * part of #MH_force_pc_resume during shutdown. 351 : */ 352 : enum GNUNET_GenericReturnValue suspended; 353 : 354 : /** 355 : * true if we already tried a forced /keys download. 356 : */ 357 : bool tried_force_keys; 358 : 359 : }; 360 : 361 : 362 : /** 363 : * Head of active pay context DLL. 364 : */ 365 : static struct PayContext *pc_head; 366 : 367 : /** 368 : * Tail of active pay context DLL. 369 : */ 370 : static struct PayContext *pc_tail; 371 : 372 : 373 : /** 374 : * Compute the timeout for a /pay request based on the number of coins 375 : * involved. 376 : * 377 : * @param num_coins number of coins 378 : * @returns timeout for the /pay request 379 : */ 380 : static struct GNUNET_TIME_Relative 381 22 : get_pay_timeout (unsigned int num_coins) 382 : { 383 : struct GNUNET_TIME_Relative t; 384 : 385 : /* FIXME: Do some benchmarking to come up with a better timeout. 386 : * We've increased this value so the wallet integration test passes again 387 : * on my (Florian) machine. 388 : */ 389 22 : t = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 390 22 : 15 * (1 + (num_coins / 5))); 391 : 392 22 : return t; 393 : } 394 : 395 : 396 : /** 397 : * Abort all pending /deposit operations. 398 : * 399 : * @param pc pay context to abort 400 : */ 401 : static void 402 44 : abort_active_deposits (struct PayContext *pc) 403 : { 404 44 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 405 : "Aborting pending /deposit operations\n"); 406 92 : for (unsigned int i = 0; icoins_cnt; i++) 407 : { 408 48 : struct DepositConfirmation *dci = &pc->dc[i]; 409 : 410 48 : if (NULL != dci->dh) 411 : { 412 0 : TALER_EXCHANGE_deposit_cancel (dci->dh); 413 0 : dci->dh = NULL; 414 : } 415 : } 416 44 : } 417 : 418 : 419 : /** 420 : * Force all pay contexts to be resumed as we are about 421 : * to shut down MHD. 422 : */ 423 : void 424 16 : TMH_force_pc_resume () 425 : { 426 16 : for (struct PayContext *pc = pc_head; 427 : NULL != pc; 428 0 : pc = pc->next) 429 : { 430 0 : abort_active_deposits (pc); 431 0 : if (NULL != pc->timeout_task) 432 : { 433 0 : GNUNET_SCHEDULER_cancel (pc->timeout_task); 434 0 : pc->timeout_task = NULL; 435 : } 436 0 : if (GNUNET_YES == pc->suspended) 437 : { 438 0 : pc->suspended = GNUNET_SYSERR; 439 0 : MHD_resume_connection (pc->connection); 440 : } 441 : } 442 16 : } 443 : 444 : 445 : /** 446 : * Resume the given pay context and send the given response. 447 : * Stores the response in the @a pc and signals MHD to resume 448 : * the connection. Also ensures MHD runs immediately. 449 : * 450 : * @param pc payment context 451 : * @param response_code response code to use 452 : * @param response response data to send back 453 : */ 454 : static void 455 22 : resume_pay_with_response (struct PayContext *pc, 456 : unsigned int response_code, 457 : struct MHD_Response *response) 458 : { 459 22 : abort_active_deposits (pc); 460 22 : pc->response_code = response_code; 461 22 : pc->response = response; 462 22 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 463 : "Resuming /pay handling as exchange interaction is done (%u)\n", 464 : response_code); 465 22 : if (NULL != pc->timeout_task) 466 : { 467 22 : GNUNET_SCHEDULER_cancel (pc->timeout_task); 468 22 : pc->timeout_task = NULL; 469 : } 470 22 : GNUNET_assert (GNUNET_YES == pc->suspended); 471 22 : pc->suspended = GNUNET_NO; 472 22 : MHD_resume_connection (pc->connection); 473 22 : TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */ 474 22 : } 475 : 476 : 477 : /** 478 : * Resume payment processing with an error. 479 : * 480 : * @param pc operation to resume 481 : * @param http_status http status code to return 482 : * @param ec taler error code to return 483 : * @param msg human readable error message 484 : */ 485 : static void 486 3 : resume_pay_with_error (struct PayContext *pc, 487 : unsigned int http_status, 488 : enum TALER_ErrorCode ec, 489 : const char *msg) 490 : { 491 3 : resume_pay_with_response (pc, 492 : http_status, 493 : TALER_MHD_make_error (ec, 494 : msg)); 495 3 : } 496 : 497 : 498 : /** 499 : * Custom cleanup routine for a struct PayContext. 500 : * 501 : * @param cls the struct PayContext to clean up. 502 : */ 503 : static void 504 22 : pay_context_cleanup (void *cls) 505 : { 506 22 : struct PayContext *pc = cls; 507 : 508 22 : if (NULL != pc->timeout_task) 509 : { 510 0 : GNUNET_SCHEDULER_cancel (pc->timeout_task); 511 0 : pc->timeout_task = NULL; 512 : } 513 22 : abort_active_deposits (pc); 514 46 : for (unsigned int i = 0; icoins_cnt; i++) 515 : { 516 24 : struct DepositConfirmation *dc = &pc->dc[i]; 517 : 518 24 : if (NULL != dc->ub_sig.rsa_signature) 519 : { 520 24 : GNUNET_CRYPTO_rsa_signature_free (dc->ub_sig.rsa_signature); 521 24 : dc->ub_sig.rsa_signature = NULL; 522 : } 523 24 : GNUNET_free (dc->exchange_url); 524 : } 525 22 : GNUNET_free (pc->dc); 526 22 : if (NULL != pc->fo) 527 : { 528 0 : TMH_EXCHANGES_find_exchange_cancel (pc->fo); 529 0 : pc->fo = NULL; 530 : } 531 22 : if (NULL != pc->response) 532 : { 533 0 : MHD_destroy_response (pc->response); 534 0 : pc->response = NULL; 535 : } 536 22 : GNUNET_free (pc->fulfillment_url); 537 22 : GNUNET_free (pc->session_id); 538 22 : GNUNET_CONTAINER_DLL_remove (pc_head, 539 : pc_tail, 540 : pc); 541 22 : GNUNET_free (pc); 542 22 : } 543 : 544 : 545 : /** 546 : * Find the exchange we need to talk to for the next 547 : * pending deposit permission. 548 : * 549 : * @param pc payment context we are processing 550 : */ 551 : static void 552 : find_next_exchange (struct PayContext *pc); 553 : 554 : 555 : /** 556 : * Execute the DB transaction. If required (from 557 : * soft/serialization errors), the transaction can be 558 : * restarted here. 559 : * 560 : * @param pc payment context to transact 561 : */ 562 : static void 563 : execute_pay_transaction (struct PayContext *pc); 564 : 565 : 566 : /** 567 : * Callback to handle a deposit permission's response. 568 : * 569 : * @param cls a struct DepositConfirmation (i.e. a pointer 570 : * into the global array of confirmations and an index for this call 571 : * in that array). That way, the last executed callback can detect 572 : * that no other confirmations are on the way, and can pack a response 573 : * for the wallet 574 : * @param hr HTTP response code details 575 : * @param deposit_timestamp time when the exchange generated the deposit confirmation 576 : * @param exchange_sig signature from the exchange over the deposit confirmation 577 : * @param exchange_pub which key did the exchange use to create the @a exchange_sig 578 : */ 579 : static void 580 22 : deposit_cb (void *cls, 581 : const struct TALER_EXCHANGE_HttpResponse *hr, 582 : struct GNUNET_TIME_Absolute deposit_timestamp, 583 : const struct TALER_ExchangeSignatureP *exchange_sig, 584 : const struct TALER_ExchangePublicKeyP *exchange_pub) 585 : { 586 22 : struct DepositConfirmation *dc = cls; 587 22 : struct PayContext *pc = dc->pc; 588 : 589 22 : dc->dh = NULL; 590 22 : GNUNET_assert (GNUNET_YES == pc->suspended); 591 22 : pc->pending_at_ce--; 592 22 : if (MHD_HTTP_OK != hr->http_status) 593 : { 594 4 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 595 : "Deposit operation failed with HTTP code %u/%d\n", 596 : hr->http_status, 597 : (int) hr->ec); 598 : /* Transaction failed */ 599 4 : if (5 == hr->http_status / 100) 600 : { 601 : /* internal server error at exchange */ 602 0 : resume_pay_with_response (pc, 603 : MHD_HTTP_BAD_GATEWAY, 604 0 : TALER_MHD_MAKE_JSON_PACK ( 605 : TALER_JSON_pack_ec ( 606 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNEXPECTED_STATUS), 607 : TMH_pack_exchange_reply (hr))); 608 : } 609 4 : else if (NULL == hr->reply) 610 : { 611 : /* We can't do anything meaningful here, the exchange did something wrong */ 612 0 : resume_pay_with_response ( 613 : pc, 614 : MHD_HTTP_BAD_GATEWAY, 615 0 : TALER_MHD_MAKE_JSON_PACK ( 616 : TALER_JSON_pack_ec ( 617 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_REPLY_MALFORMED), 618 : TMH_pack_exchange_reply (hr))); 619 : } 620 : else 621 : { 622 : /* Forward error, adding the "coin_pub" for which the 623 : error was being generated */ 624 4 : if (TALER_EC_EXCHANGE_DEPOSIT_INSUFFICIENT_FUNDS == hr->ec) 625 : { 626 4 : resume_pay_with_response ( 627 : pc, 628 : MHD_HTTP_CONFLICT, 629 4 : TALER_MHD_MAKE_JSON_PACK ( 630 : TALER_JSON_pack_ec ( 631 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_FUNDS), 632 : TMH_pack_exchange_reply (hr), 633 : GNUNET_JSON_pack_data_auto ("coin_pub", 634 : &dc->coin_pub))); 635 : } 636 : else 637 : { 638 0 : resume_pay_with_response ( 639 : pc, 640 : MHD_HTTP_BAD_GATEWAY, 641 0 : TALER_MHD_MAKE_JSON_PACK ( 642 : TALER_JSON_pack_ec ( 643 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNEXPECTED_STATUS), 644 : TMH_pack_exchange_reply (hr), 645 : GNUNET_JSON_pack_data_auto ("coin_pub", 646 : &dc->coin_pub))); 647 : } 648 : } 649 4 : return; 650 : } 651 : 652 : /* store result to DB */ 653 18 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 654 : "Storing successful payment %s (%s) at instance %s'\n", 655 : pc->hc->infix, 656 : GNUNET_h2s (&pc->h_contract_terms), 657 : pc->hc->instance->settings.id); 658 18 : TMH_db->preflight (TMH_db->cls); 659 : { 660 : enum GNUNET_DB_QueryStatus qs; 661 : 662 18 : qs = TMH_db->insert_deposit (TMH_db->cls, 663 18 : pc->hc->instance->settings.id, 664 : deposit_timestamp, 665 18 : &pc->h_contract_terms, 666 18 : &dc->coin_pub, 667 18 : dc->exchange_url, 668 18 : &dc->amount_with_fee, 669 18 : &dc->deposit_fee, 670 18 : &dc->refund_fee, 671 18 : &dc->wire_fee, 672 18 : &pc->wm->h_wire, 673 : exchange_sig, 674 : exchange_pub); 675 18 : if (0 > qs) 676 : { 677 : /* Special report if retries insufficient */ 678 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs) 679 : { 680 0 : execute_pay_transaction (pc); 681 0 : return; 682 : } 683 : /* Always report on hard error as well to enable diagnostics */ 684 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); 685 : /* Forward error including 'proof' for the body */ 686 0 : resume_pay_with_error (pc, 687 : MHD_HTTP_INTERNAL_SERVER_ERROR, 688 : TALER_EC_GENERIC_DB_STORE_FAILED, 689 : "deposit"); 690 0 : return; 691 : } 692 : } 693 : 694 18 : dc->found_in_db = true; /* well, at least NOW it'd be true ;-) */ 695 18 : pc->pending--; 696 : 697 18 : if (0 != pc->pending_at_ce) 698 1 : return; /* still more to do with current exchange */ 699 17 : find_next_exchange (pc); 700 : } 701 : 702 : 703 : /** 704 : * Function called with the result of our exchange lookup. 705 : * 706 : * @param cls the struct PayContext 707 : * @param hr HTTP response details 708 : * @param exchange_handle NULL if exchange was not found to be acceptable 709 : * @param payto_uri payto://-URI of the exchange 710 : * @param wire_fee current applicable fee for dealing with @a exchange_handle, 711 : * NULL if not available 712 : * @param exchange_trusted true if this exchange is 713 : * trusted by config 714 : */ 715 : static void 716 21 : process_pay_with_exchange (void *cls, 717 : const struct TALER_EXCHANGE_HttpResponse *hr, 718 : struct TALER_EXCHANGE_Handle *exchange_handle, 719 : const char *payto_uri, 720 : const struct TALER_Amount *wire_fee, 721 : bool exchange_trusted) 722 : { 723 21 : struct PayContext *pc = cls; 724 21 : struct TMH_HandlerContext *hc = pc->hc; 725 : const struct TALER_EXCHANGE_Keys *keys; 726 : 727 : (void) payto_uri; 728 21 : pc->fo = NULL; 729 21 : GNUNET_assert (GNUNET_YES == pc->suspended); 730 21 : if (NULL == hr) 731 : { 732 0 : resume_pay_with_response ( 733 : pc, 734 : MHD_HTTP_GATEWAY_TIMEOUT, 735 0 : TALER_MHD_MAKE_JSON_PACK ( 736 : TALER_JSON_pack_ec (TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT))); 737 0 : return; 738 : } 739 21 : if ( (MHD_HTTP_OK != hr->http_status) || 740 : (NULL == exchange_handle) ) 741 : { 742 0 : resume_pay_with_response ( 743 : pc, 744 : MHD_HTTP_BAD_GATEWAY, 745 0 : TALER_MHD_MAKE_JSON_PACK ( 746 : TALER_JSON_pack_ec ( 747 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_CONNECT_FAILURE), 748 : TMH_pack_exchange_reply (hr))); 749 0 : return; 750 : } 751 21 : keys = TALER_EXCHANGE_get_keys (exchange_handle); 752 21 : if (NULL == keys) 753 : { 754 0 : GNUNET_break (0); /* should not be possible if HTTP status is #MHD_HTTP_OK */ 755 0 : resume_pay_with_error (pc, 756 : MHD_HTTP_BAD_GATEWAY, 757 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_KEYS_FAILURE, 758 : NULL); 759 0 : return; 760 : } 761 : 762 : /* Initiate /deposit operation for all coins of 763 : the current exchange (!) */ 764 21 : GNUNET_assert (0 == pc->pending_at_ce); 765 44 : for (unsigned int i = 0; icoins_cnt; i++) 766 : { 767 23 : struct DepositConfirmation *dc = &pc->dc[i]; 768 : const struct TALER_EXCHANGE_DenomPublicKey *denom_details; 769 : enum TALER_ErrorCode ec; 770 : unsigned int http_status; 771 : 772 23 : if (NULL != dc->dh) 773 1 : continue; /* we were here before (can happen due to 774 : tried_force_keys logic), don't go again */ 775 23 : if (dc->found_in_db) 776 1 : continue; 777 22 : if (0 != strcmp (dc->exchange_url, 778 : pc->current_exchange)) 779 0 : continue; 780 : denom_details 781 22 : = TALER_EXCHANGE_get_denomination_key_by_hash (keys, 782 22 : &dc->h_denom); 783 22 : if (NULL == denom_details) 784 : { 785 0 : if (! pc->tried_force_keys) 786 : { 787 : /* let's try *forcing* a re-download of /keys from the exchange. 788 : Maybe the wallet has seen /keys that we missed. */ 789 0 : pc->tried_force_keys = true; 790 0 : pc->fo = TMH_EXCHANGES_find_exchange (pc->current_exchange, 791 0 : pc->wm->wire_method, 792 : GNUNET_YES, 793 : &process_pay_with_exchange, 794 : pc); 795 0 : if (NULL != pc->fo) 796 0 : return; 797 : } 798 : /* Forcing failed or we already did it, give up */ 799 0 : resume_pay_with_response ( 800 : pc, 801 : MHD_HTTP_BAD_REQUEST, 802 0 : TALER_MHD_MAKE_JSON_PACK ( 803 : TALER_JSON_pack_ec ( 804 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_NOT_FOUND), 805 : GNUNET_JSON_pack_data_auto ("h_denom_pub", 806 : &dc->h_denom), 807 : GNUNET_JSON_pack_allow_null ( 808 : GNUNET_JSON_pack_object_incref ( 809 : "exchange_keys", 810 : (json_t *) TALER_EXCHANGE_get_keys_raw (exchange_handle))))); 811 0 : return; 812 : } 813 22 : if (GNUNET_OK != 814 22 : TMH_AUDITORS_check_dk (exchange_handle, 815 : denom_details, 816 : exchange_trusted, 817 : &http_status, 818 : &ec)) 819 : { 820 0 : if (! pc->tried_force_keys) 821 : { 822 : /* let's try *forcing* a re-download of /keys from the exchange. 823 : Maybe the wallet has seen auditors that we missed. */ 824 0 : pc->tried_force_keys = true; 825 0 : pc->fo = TMH_EXCHANGES_find_exchange (pc->current_exchange, 826 0 : pc->wm->wire_method, 827 : GNUNET_YES, 828 : &process_pay_with_exchange, 829 : pc); 830 0 : if (NULL != pc->fo) 831 0 : return; 832 : } 833 0 : resume_pay_with_response ( 834 : pc, 835 : http_status, 836 0 : TALER_MHD_MAKE_JSON_PACK ( 837 : TALER_JSON_pack_ec (ec), 838 : GNUNET_JSON_pack_data_auto ("h_denom_pub", 839 : &denom_details->h_key))); 840 0 : return; 841 : } 842 : 843 22 : dc->deposit_fee = denom_details->fee_deposit; 844 22 : dc->refund_fee = denom_details->fee_refund; 845 22 : dc->wire_fee = *wire_fee; 846 22 : GNUNET_assert (NULL != pc->wm); 847 22 : GNUNET_assert (NULL != pc->wm->j_wire); 848 22 : TMH_db->preflight (TMH_db->cls); 849 44 : dc->dh = TALER_EXCHANGE_deposit (exchange_handle, 850 22 : &dc->amount_with_fee, 851 : pc->wire_transfer_deadline, 852 22 : pc->wm->j_wire, 853 22 : &pc->h_contract_terms, 854 22 : &dc->coin_pub, 855 22 : &dc->ub_sig, 856 : &denom_details->key, 857 : pc->timestamp, 858 22 : &hc->instance->merchant_pub, 859 : pc->refund_deadline, 860 22 : &dc->coin_sig, 861 : &deposit_cb, 862 : dc, 863 : &ec); 864 22 : if (NULL == dc->dh) 865 : { 866 : /* Signature was invalid or some other constraint was not satisfied. If 867 : the exchange was unavailable, we'd get that information in the 868 : callback. */ 869 0 : GNUNET_break_op (0); 870 0 : resume_pay_with_response ( 871 : pc, 872 : TALER_ErrorCode_get_http_status_safe (ec), 873 0 : TALER_MHD_MAKE_JSON_PACK ( 874 : TALER_JSON_pack_ec (ec), 875 : GNUNET_JSON_pack_uint64 ("coin_idx", 876 : i))); 877 0 : return; 878 : } 879 22 : if (TMH_force_audit) 880 0 : TALER_EXCHANGE_deposit_force_dc (dc->dh); 881 22 : pc->pending_at_ce++; 882 : } 883 : } 884 : 885 : 886 : /** 887 : * Find the exchange we need to talk to for the next 888 : * pending deposit permission. 889 : * 890 : * @param pc payment context we are processing 891 : */ 892 : static void 893 38 : find_next_exchange (struct PayContext *pc) 894 : { 895 38 : GNUNET_assert (0 == pc->pending_at_ce); 896 57 : for (unsigned int i = 0; icoins_cnt; i++) 897 : { 898 40 : struct DepositConfirmation *dc = &pc->dc[i]; 899 : 900 40 : if (dc->found_in_db) 901 19 : continue; 902 : 903 21 : pc->current_exchange = dc->exchange_url; 904 42 : pc->fo = TMH_EXCHANGES_find_exchange (pc->current_exchange, 905 21 : pc->wm->wire_method, 906 : GNUNET_NO, 907 : &process_pay_with_exchange, 908 : pc); 909 21 : if (NULL == pc->fo) 910 : { 911 0 : GNUNET_break (0); 912 0 : resume_pay_with_error (pc, 913 : MHD_HTTP_INTERNAL_SERVER_ERROR, 914 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_LOOKUP_FAILED, 915 : "Failed to lookup exchange by URL"); 916 0 : return; 917 : } 918 21 : return; 919 : } 920 17 : pc->current_exchange = NULL; 921 : /* We are done with all the HTTP requests, go back and try 922 : the 'big' database transaction! (It should work now!) */ 923 17 : GNUNET_assert (0 == pc->pending); 924 17 : execute_pay_transaction (pc); 925 : } 926 : 927 : 928 : /** 929 : * Function called with information about a coin that was deposited. 930 : * 931 : * @param cls closure 932 : * @param exchange_url exchange where @a coin_pub was deposited 933 : * @param coin_pub public key of the coin 934 : * @param amount_with_fee amount the exchange will deposit for this coin 935 : * @param deposit_fee fee the exchange will charge for this coin 936 : * @param refund_fee fee the exchange will charge for refunding this coin 937 : * @param wire_fee wire fee the exchange of this coin charges 938 : */ 939 : static void 940 21 : check_coin_paid (void *cls, 941 : const char *exchange_url, 942 : const struct TALER_CoinSpendPublicKeyP *coin_pub, 943 : const struct TALER_Amount *amount_with_fee, 944 : const struct TALER_Amount *deposit_fee, 945 : const struct TALER_Amount *refund_fee, 946 : const struct TALER_Amount *wire_fee) 947 : { 948 21 : struct PayContext *pc = cls; 949 : 950 45 : for (unsigned int i = 0; icoins_cnt; i++) 951 : { 952 24 : struct DepositConfirmation *dc = &pc->dc[i]; 953 : 954 24 : if (dc->found_in_db) 955 1 : continue; /* processed earlier, skip "expensive" memcmp() */ 956 : /* Get matching coin from results*/ 957 23 : if ( (0 != GNUNET_memcmp (coin_pub, 958 20 : &dc->coin_pub)) || 959 : (0 != 960 20 : strcmp (exchange_url, 961 40 : dc->exchange_url)) || 962 20 : (0 != TALER_amount_cmp (amount_with_fee, 963 20 : &dc->amount_with_fee)) ) 964 3 : continue; /* does not match, skip */ 965 20 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 966 : "Deposit of coin %s' already in our DB.\n", 967 : TALER_B2S (coin_pub)); 968 : 969 20 : GNUNET_assert (0 <= 970 : TALER_amount_add (&pc->total_paid, 971 : &pc->total_paid, 972 : amount_with_fee)); 973 20 : GNUNET_assert (0 <= 974 : TALER_amount_add (&pc->total_fees_paid, 975 : &pc->total_fees_paid, 976 : deposit_fee)); 977 20 : dc->deposit_fee = *deposit_fee; 978 20 : dc->refund_fee = *refund_fee; 979 20 : dc->wire_fee = *wire_fee; 980 20 : dc->amount_with_fee = *amount_with_fee; 981 20 : dc->found_in_db = true; 982 20 : pc->pending--; 983 : } 984 21 : } 985 : 986 : 987 : /** 988 : * Function called with information about a refund. Check if this coin was 989 : * claimed by the wallet for the transaction, and if so add the refunded 990 : * amount to the pc's "total_refunded" amount. 991 : * 992 : * @param cls closure with a struct PayContext 993 : * @param coin_pub public coin from which the refund comes from 994 : * @param refund_amount refund amount which is being taken from @a coin_pub 995 : */ 996 : static void 997 0 : check_coin_refunded (void *cls, 998 : const struct TALER_CoinSpendPublicKeyP *coin_pub, 999 : const struct TALER_Amount *refund_amount) 1000 : { 1001 0 : struct PayContext *pc = cls; 1002 : 1003 : /* We look at refunds here that apply to the coins 1004 : that the customer is currently trying to pay us with. 1005 : 1006 : Such refunds are not "normal" refunds, but abort-pay refunds, which are 1007 : given in the case that the wallet aborts the payment. 1008 : In the case the wallet then decides to complete the payment *after* doing 1009 : an abort-pay refund (an unusual but possible case), we need 1010 : to make sure that existing refunds are accounted for. */ 1011 : 1012 0 : for (unsigned int i = 0; icoins_cnt; i++) 1013 : { 1014 0 : struct DepositConfirmation *dc = &pc->dc[i]; 1015 : /* Get matching coins from results. */ 1016 0 : if (0 != GNUNET_memcmp (coin_pub, 1017 : &dc->coin_pub)) 1018 0 : continue; 1019 0 : GNUNET_assert (0 <= 1020 : TALER_amount_add (&pc->total_refunded, 1021 : &pc->total_refunded, 1022 : refund_amount)); 1023 0 : break; 1024 : } 1025 0 : } 1026 : 1027 : 1028 : /** 1029 : * Check whether the amount paid is sufficient to cover the price. 1030 : * 1031 : * @param pc payment context to check 1032 : * @return true if the payment is sufficient, false if it is 1033 : * insufficient 1034 : */ 1035 : static bool 1036 18 : check_payment_sufficient (struct PayContext *pc) 1037 : { 1038 : struct TALER_Amount acc_fee; 1039 : struct TALER_Amount acc_amount; 1040 : struct TALER_Amount final_amount; 1041 : struct TALER_Amount wire_fee_delta; 1042 : struct TALER_Amount wire_fee_customer_contribution; 1043 : struct TALER_Amount total_wire_fee; 1044 : struct TALER_Amount total_needed; 1045 : 1046 18 : if (0 == pc->coins_cnt) 1047 : { 1048 0 : return ((0 == pc->amount.value) && 1049 0 : (0 == pc->amount.fraction)); 1050 : } 1051 : 1052 18 : acc_fee = pc->dc[0].deposit_fee; 1053 18 : total_wire_fee = pc->dc[0].wire_fee; 1054 18 : acc_amount = pc->dc[0].amount_with_fee; 1055 : 1056 : /** 1057 : * This loops calculates what are the deposit fee / total 1058 : * amount with fee / and wire fee, for all the coins. 1059 : */ 1060 19 : for (unsigned int i = 1; icoins_cnt; i++) 1061 : { 1062 1 : struct DepositConfirmation *dc = &pc->dc[i]; 1063 : 1064 1 : GNUNET_assert (dc->found_in_db); 1065 1 : if ( (0 > 1066 1 : TALER_amount_add (&acc_fee, 1067 1 : &dc->deposit_fee, 1068 1 : &acc_fee)) || 1069 : (0 > 1070 1 : TALER_amount_add (&acc_amount, 1071 1 : &dc->amount_with_fee, 1072 : &acc_amount)) ) 1073 : { 1074 0 : GNUNET_break (0); 1075 : /* Overflow in these amounts? Very strange. */ 1076 0 : resume_pay_with_error (pc, 1077 : MHD_HTTP_INTERNAL_SERVER_ERROR, 1078 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW, 1079 : "Overflow adding up amounts"); 1080 0 : return false; 1081 : } 1082 1 : if (1 == 1083 1 : TALER_amount_cmp (&dc->deposit_fee, 1084 1 : &dc->amount_with_fee)) 1085 : { 1086 0 : GNUNET_break_op (0); 1087 0 : resume_pay_with_error (pc, 1088 : MHD_HTTP_BAD_REQUEST, 1089 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_FEES_EXCEED_PAYMENT, 1090 : "Deposit fees exceed coin's contribution"); 1091 0 : return false; 1092 : } 1093 : 1094 : /* If exchange differs, add wire fee */ 1095 : { 1096 1 : bool new_exchange = true; 1097 : 1098 1 : for (unsigned int j = 0; jexchange_url, 1100 1 : pc->dc[j].exchange_url)) 1101 : { 1102 1 : new_exchange = false; 1103 1 : break; 1104 : } 1105 : 1106 1 : if (! new_exchange) 1107 1 : continue; 1108 : 1109 0 : if (GNUNET_OK != 1110 0 : TALER_amount_cmp_currency (&total_wire_fee, 1111 0 : &dc->wire_fee)) 1112 : { 1113 0 : GNUNET_break_op (0); 1114 0 : resume_pay_with_error (pc, 1115 : MHD_HTTP_CONFLICT, 1116 : TALER_EC_GENERIC_CURRENCY_MISMATCH, 1117 : total_wire_fee.currency); 1118 0 : return false; 1119 : } 1120 0 : if (0 > 1121 0 : TALER_amount_add (&total_wire_fee, 1122 : &total_wire_fee, 1123 0 : &dc->wire_fee)) 1124 : { 1125 0 : GNUNET_break (0); 1126 0 : resume_pay_with_error (pc, 1127 : MHD_HTTP_INTERNAL_SERVER_ERROR, 1128 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_WIRE_FEE_ADDITION_FAILED, 1129 : "could not add exchange wire fee to total"); 1130 0 : return false; 1131 : } 1132 : } 1133 : } /* deposit loop */ 1134 : 1135 18 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1136 : "Amount received from wallet: %s\n", 1137 : TALER_amount2s (&acc_amount)); 1138 18 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1139 : "Deposit fee for all coins: %s\n", 1140 : TALER_amount2s (&acc_fee)); 1141 18 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1142 : "Total wire fee: %s\n", 1143 : TALER_amount2s (&total_wire_fee)); 1144 18 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1145 : "Max wire fee: %s\n", 1146 : TALER_amount2s (&pc->max_wire_fee)); 1147 18 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1148 : "Deposit fee limit for merchant: %s\n", 1149 : TALER_amount2s (&pc->max_fee)); 1150 18 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1151 : "Total refunded amount: %s\n", 1152 : TALER_amount2s (&pc->total_refunded)); 1153 : 1154 : /* Now compare exchange wire fee compared to 1155 : * what we are willing to pay */ 1156 18 : if (GNUNET_YES != 1157 18 : TALER_amount_cmp_currency (&total_wire_fee, 1158 18 : &pc->max_wire_fee)) 1159 : { 1160 0 : GNUNET_break (0); 1161 0 : resume_pay_with_error (pc, 1162 : MHD_HTTP_CONFLICT, 1163 : TALER_EC_GENERIC_CURRENCY_MISMATCH, 1164 : total_wire_fee.currency); 1165 0 : return false; 1166 : } 1167 : 1168 18 : switch (TALER_amount_subtract (&wire_fee_delta, 1169 : &total_wire_fee, 1170 18 : &pc->max_wire_fee)) 1171 : { 1172 0 : case TALER_AAR_RESULT_POSITIVE: 1173 : /* Actual wire fee is indeed higher than our maximum, 1174 : compute how much the customer is expected to cover! */ 1175 0 : TALER_amount_divide (&wire_fee_customer_contribution, 1176 : &wire_fee_delta, 1177 : pc->wire_fee_amortization); 1178 0 : break; 1179 18 : case TALER_AAR_RESULT_ZERO: 1180 : case TALER_AAR_INVALID_NEGATIVE_RESULT: 1181 : /* Wire fee threshold is still above the wire fee amount. 1182 : Customer is not going to contribute on this. */ 1183 18 : GNUNET_assert (GNUNET_OK == 1184 : TALER_amount_set_zero (total_wire_fee.currency, 1185 : &wire_fee_customer_contribution)); 1186 18 : break; 1187 0 : default: 1188 0 : GNUNET_assert (0); 1189 : } 1190 : 1191 : /* add wire fee contribution to the total fees */ 1192 18 : if (0 > 1193 18 : TALER_amount_add (&acc_fee, 1194 : &acc_fee, 1195 : &wire_fee_customer_contribution)) 1196 : { 1197 0 : GNUNET_break (0); 1198 0 : resume_pay_with_error (pc, 1199 : MHD_HTTP_INTERNAL_SERVER_ERROR, 1200 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW, 1201 : "Overflow adding up amounts"); 1202 0 : return false; 1203 : } 1204 18 : if (-1 == TALER_amount_cmp (&pc->max_fee, 1205 : &acc_fee)) 1206 : { 1207 : /** 1208 : * Sum of fees of *all* the different exchanges of all the coins are 1209 : * higher than the fixed limit that the merchant is willing to pay. The 1210 : * difference must be paid by the customer. 1211 : */// 1212 : struct TALER_Amount excess_fee; 1213 : 1214 : /* compute fee amount to be covered by customer */ 1215 0 : GNUNET_assert (TALER_AAR_RESULT_POSITIVE == 1216 : TALER_amount_subtract (&excess_fee, 1217 : &acc_fee, 1218 : &pc->max_fee)); 1219 : /* add that to the total */ 1220 0 : if (0 > 1221 0 : TALER_amount_add (&total_needed, 1222 : &excess_fee, 1223 0 : &pc->amount)) 1224 : { 1225 0 : GNUNET_break (0); 1226 0 : resume_pay_with_error (pc, 1227 : MHD_HTTP_INTERNAL_SERVER_ERROR, 1228 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW, 1229 : "Overflow adding up amounts"); 1230 0 : return false; 1231 : } 1232 : } 1233 : else 1234 : { 1235 : /* Fees are fully covered by the merchant, all we require 1236 : is that the total payment is not below the contract's amount */ 1237 18 : total_needed = pc->amount; 1238 : } 1239 : 1240 : /* Do not count refunds towards the payment */ 1241 18 : GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1242 : "Subtracting total refunds from paid amount: %s\n", 1243 : TALER_amount2s (&pc->total_refunded)); 1244 18 : if (0 > 1245 18 : TALER_amount_subtract (&final_amount, 1246 : &acc_amount, 1247 18 : &pc->total_refunded)) 1248 : { 1249 0 : GNUNET_break (0); 1250 0 : resume_pay_with_error (pc, 1251 : MHD_HTTP_INTERNAL_SERVER_ERROR, 1252 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_REFUNDS_EXCEED_PAYMENTS, 1253 : "refunded amount exceeds total payments"); 1254 0 : return false; 1255 : } 1256 : 1257 18 : if (-1 == TALER_amount_cmp (&final_amount, 1258 : &total_needed)) 1259 : { 1260 : /* acc_amount < total_needed */ 1261 3 : if (-1 < TALER_amount_cmp (&acc_amount, 1262 : &total_needed)) 1263 : { 1264 0 : GNUNET_break_op (0); 1265 0 : resume_pay_with_error (pc, 1266 : MHD_HTTP_PAYMENT_REQUIRED, 1267 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_REFUNDED, 1268 : "contract not paid up due to refunds"); 1269 : } 1270 3 : else if (-1 < TALER_amount_cmp (&acc_amount, 1271 3 : &pc->amount)) 1272 : { 1273 0 : GNUNET_break_op (0); 1274 0 : resume_pay_with_error (pc, 1275 : MHD_HTTP_NOT_ACCEPTABLE, 1276 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_DUE_TO_FEES, 1277 : "contract not paid up due to fees (client may have calculated them badly)"); 1278 : } 1279 : else 1280 : { 1281 3 : GNUNET_break_op (0); 1282 3 : resume_pay_with_error (pc, 1283 : MHD_HTTP_NOT_ACCEPTABLE, 1284 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_PAYMENT_INSUFFICIENT, 1285 : "payment insufficient"); 1286 : } 1287 3 : return false; 1288 : } 1289 15 : return true; 1290 : } 1291 : 1292 : 1293 : /** 1294 : * Use database to notify other clients about the 1295 : * payment being completed. 1296 : * 1297 : * @param pc context to trigger notification for 1298 : */ 1299 : static void 1300 15 : trigger_payment_notification (struct PayContext *pc) 1301 : { 1302 : { 1303 15 : struct TMH_OrderPayEventP pay_eh = { 1304 15 : .header.size = htons (sizeof (pay_eh)), 1305 15 : .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_PAID), 1306 15 : .merchant_pub = pc->hc->instance->merchant_pub 1307 : }; 1308 : 1309 15 : GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1310 : "Notifying clients about payment of order %s\n", 1311 : pc->order_id); 1312 15 : GNUNET_CRYPTO_hash (pc->order_id, 1313 : strlen (pc->order_id), 1314 : &pay_eh.h_order_id); 1315 15 : TMH_db->event_notify (TMH_db->cls, 1316 : &pay_eh.header, 1317 : NULL, 1318 : 0); 1319 : } 1320 15 : if ( (NULL != pc->session_id) && 1321 15 : (NULL != pc->fulfillment_url) ) 1322 : { 1323 14 : struct TMH_SessionEventP session_eh = { 1324 14 : .header.size = htons (sizeof (session_eh)), 1325 14 : .header.type = htons (TALER_DBEVENT_MERCHANT_SESSION_CAPTURED), 1326 14 : .merchant_pub = pc->hc->instance->merchant_pub 1327 : }; 1328 : 1329 14 : GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1330 : "Notifying clients about session change to %s for %s\n", 1331 : pc->session_id, 1332 : pc->fulfillment_url); 1333 14 : GNUNET_CRYPTO_hash (pc->session_id, 1334 14 : strlen (pc->session_id), 1335 : &session_eh.h_session_id); 1336 14 : GNUNET_CRYPTO_hash (pc->fulfillment_url, 1337 14 : strlen (pc->fulfillment_url), 1338 : &session_eh.h_fulfillment_url); 1339 14 : TMH_db->event_notify (TMH_db->cls, 1340 : &session_eh.header, 1341 : NULL, 1342 : 0); 1343 : } 1344 15 : } 1345 : 1346 : 1347 : /** 1348 : * 1349 : * 1350 : */ 1351 : static void 1352 39 : execute_pay_transaction (struct PayContext *pc) 1353 : { 1354 39 : struct TMH_HandlerContext *hc = pc->hc; 1355 39 : const char *instance_id = hc->instance->settings.id; 1356 : 1357 : /* Avoid re-trying transactions on soft errors forever! */ 1358 39 : if (pc->retry_counter++ > MAX_RETRIES) 1359 : { 1360 0 : GNUNET_break (0); 1361 0 : resume_pay_with_error (pc, 1362 : MHD_HTTP_INTERNAL_SERVER_ERROR, 1363 : TALER_EC_GENERIC_DB_SOFT_FAILURE, 1364 : NULL); 1365 0 : return; 1366 : } 1367 39 : GNUNET_assert (GNUNET_YES == pc->suspended); 1368 : 1369 : /* Initialize some amount accumulators 1370 : (used in check_coin_paid(), check_coin_refunded() 1371 : and check_payment_sufficient()). */ 1372 39 : GNUNET_break (GNUNET_OK == 1373 : TALER_amount_set_zero (pc->amount.currency, 1374 : &pc->total_paid)); 1375 39 : GNUNET_break (GNUNET_OK == 1376 : TALER_amount_set_zero (pc->amount.currency, 1377 : &pc->total_fees_paid)); 1378 39 : GNUNET_break (GNUNET_OK == 1379 : TALER_amount_set_zero (pc->amount.currency, 1380 : &pc->total_refunded)); 1381 81 : for (unsigned int i = 0; icoins_cnt; i++) 1382 42 : pc->dc[i].found_in_db = false; 1383 39 : pc->pending = pc->coins_cnt; 1384 : 1385 : /* First, try to see if we have all we need already done */ 1386 39 : TMH_db->preflight (TMH_db->cls); 1387 39 : if (GNUNET_OK != 1388 39 : TMH_db->start (TMH_db->cls, 1389 : "run pay")) 1390 : { 1391 0 : GNUNET_break (0); 1392 0 : resume_pay_with_error (pc, 1393 : MHD_HTTP_INTERNAL_SERVER_ERROR, 1394 : TALER_EC_GENERIC_DB_START_FAILED, 1395 : NULL); 1396 0 : return; 1397 : } 1398 : 1399 : { 1400 : enum GNUNET_DB_QueryStatus qs; 1401 : 1402 : /* Check if some of these coins already succeeded for _this_ contract. */ 1403 39 : qs = TMH_db->lookup_deposits (TMH_db->cls, 1404 : instance_id, 1405 39 : &pc->h_contract_terms, 1406 : &check_coin_paid, 1407 : pc); 1408 39 : if (0 > qs) 1409 : { 1410 0 : TMH_db->rollback (TMH_db->cls); 1411 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs) 1412 : { 1413 0 : execute_pay_transaction (pc); 1414 0 : return; 1415 : } 1416 : /* Always report on hard error as well to enable diagnostics */ 1417 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); 1418 0 : resume_pay_with_error (pc, 1419 : MHD_HTTP_INTERNAL_SERVER_ERROR, 1420 : TALER_EC_GENERIC_DB_FETCH_FAILED, 1421 : "lookup deposits"); 1422 0 : return; 1423 : } 1424 : } 1425 : 1426 : 1427 : { 1428 : enum GNUNET_DB_QueryStatus qs; 1429 : 1430 : /* Check if we refunded some of the coins */ 1431 39 : qs = TMH_db->lookup_refunds (TMH_db->cls, 1432 : instance_id, 1433 39 : &pc->h_contract_terms, 1434 : &check_coin_refunded, 1435 : pc); 1436 39 : if (0 > qs) 1437 : { 1438 0 : TMH_db->rollback (TMH_db->cls); 1439 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs) 1440 : { 1441 0 : execute_pay_transaction (pc); 1442 0 : return; 1443 : } 1444 : /* Always report on hard error as well to enable diagnostics */ 1445 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); 1446 0 : resume_pay_with_error (pc, 1447 : MHD_HTTP_INTERNAL_SERVER_ERROR, 1448 : TALER_EC_GENERIC_DB_FETCH_FAILED, 1449 : "lookup refunds"); 1450 0 : return; 1451 : } 1452 : } 1453 : 1454 : /* Check if there are coins that still need to be processed */ 1455 39 : if (0 != pc->pending) 1456 : { 1457 : /* we made no DB changes, so we can just rollback */ 1458 21 : TMH_db->rollback (TMH_db->cls); 1459 : 1460 : /* Ok, we need to first go to the network to process more coins. 1461 : We that interaction in *tiny* transactions (hence the rollback 1462 : above). */ 1463 21 : find_next_exchange (pc); 1464 21 : return; 1465 : } 1466 : 1467 : /* 0 == pc->pending: all coins processed, let's see if that was enough */ 1468 18 : if (! check_payment_sufficient (pc)) 1469 : { 1470 : /* check_payment_sufficient() will have queued an error already. 1471 : We need to still abort the transaction. */ 1472 3 : TMH_db->rollback (TMH_db->cls); 1473 3 : return; 1474 : } 1475 : /* Payment succeeded, save in database */ 1476 15 : GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1477 : "Order %s' (%s) was fully paid\n", 1478 : pc->order_id, 1479 : GNUNET_h2s (&pc->h_contract_terms)); 1480 : { 1481 : enum GNUNET_DB_QueryStatus qs; 1482 : 1483 15 : qs = TMH_db->mark_contract_paid (TMH_db->cls, 1484 : instance_id, 1485 15 : &pc->h_contract_terms, 1486 15 : pc->session_id); 1487 15 : if (qs < 0) 1488 : { 1489 0 : TMH_db->rollback (TMH_db->cls); 1490 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs) 1491 : { 1492 0 : execute_pay_transaction (pc); 1493 0 : return; 1494 : } 1495 0 : GNUNET_break (0); 1496 0 : resume_pay_with_error (pc, 1497 : MHD_HTTP_INTERNAL_SERVER_ERROR, 1498 : TALER_EC_GENERIC_DB_STORE_FAILED, 1499 : "mark contract paid"); 1500 0 : return; 1501 : } 1502 : } 1503 : 1504 : { 1505 : enum GNUNET_DB_QueryStatus qs; 1506 : 1507 : /* Now commit! */ 1508 15 : qs = TMH_db->commit (TMH_db->cls); 1509 15 : if (0 > qs) 1510 : { 1511 : /* commit failed */ 1512 0 : TMH_db->rollback (TMH_db->cls); 1513 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs) 1514 : { 1515 0 : execute_pay_transaction (pc); 1516 0 : return; 1517 : } 1518 0 : GNUNET_break (0); 1519 0 : resume_pay_with_error (pc, 1520 : MHD_HTTP_INTERNAL_SERVER_ERROR, 1521 : TALER_EC_GENERIC_DB_COMMIT_FAILED, 1522 : NULL); 1523 0 : return; 1524 : } 1525 15 : trigger_payment_notification (pc); 1526 : } 1527 : 1528 : /* Generate response (payment successful) */ 1529 : { 1530 : struct GNUNET_CRYPTO_EddsaSignature sig; 1531 : 1532 : /* Sign on our end (as the payment did go through, even if it may 1533 : have been refunded already) */ 1534 : { 1535 15 : struct TALER_PaymentResponsePS mr = { 1536 15 : .purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_PAYMENT_OK), 1537 15 : .purpose.size = htonl (sizeof (mr)), 1538 : .h_contract_terms = pc->h_contract_terms 1539 : }; 1540 : 1541 15 : GNUNET_CRYPTO_eddsa_sign (&pc->hc->instance->merchant_priv.eddsa_priv, 1542 : &mr, 1543 : &sig); 1544 : } 1545 : 1546 : /* Build the response */ 1547 15 : resume_pay_with_response ( 1548 : pc, 1549 : MHD_HTTP_OK, 1550 15 : TALER_MHD_MAKE_JSON_PACK ( 1551 : GNUNET_JSON_pack_data_auto ("sig", 1552 : &sig))); 1553 : } 1554 : } 1555 : 1556 : 1557 : /** 1558 : * Try to parse the pay request into the given pay context. 1559 : * Schedules an error response in the connection on failure. 1560 : * 1561 : * @param connection HTTP connection we are receiving payment on 1562 : * @param[in,out] hc context with further information about the request 1563 : * @param pc context we use to handle the payment 1564 : * @return #GNUNET_OK on success, 1565 : * #GNUNET_NO on failure (response was queued with MHD) 1566 : * #GNUNET_SYSERR on hard error (MHD connection must be dropped) 1567 : */ 1568 : static enum GNUNET_GenericReturnValue 1569 22 : parse_pay (struct MHD_Connection *connection, 1570 : struct TMH_HandlerContext *hc, 1571 : struct PayContext *pc) 1572 : { 1573 : /* First, parse request */ 1574 : { 1575 22 : const char *session_id = NULL; 1576 : json_t *coins; 1577 : struct GNUNET_JSON_Specification spec[] = { 1578 22 : GNUNET_JSON_spec_json ("coins", 1579 : &coins), 1580 22 : GNUNET_JSON_spec_mark_optional ( 1581 : GNUNET_JSON_spec_string ("session_id", 1582 : &session_id)), 1583 22 : GNUNET_JSON_spec_end () 1584 : }; 1585 : 1586 : { 1587 : enum GNUNET_GenericReturnValue res; 1588 : 1589 22 : res = TALER_MHD_parse_json_data (connection, 1590 22 : hc->request_body, 1591 : spec); 1592 22 : if (GNUNET_YES != res) 1593 : { 1594 0 : GNUNET_break_op (0); 1595 0 : return res; 1596 : } 1597 : } 1598 : 1599 : /* copy session ID (if set) */ 1600 22 : if (NULL != session_id) 1601 : { 1602 11 : pc->session_id = GNUNET_strdup (session_id); 1603 : } 1604 : else 1605 : { 1606 : /* use empty string as default if client didn't specify it */ 1607 11 : pc->session_id = GNUNET_strdup (""); 1608 : } 1609 : 1610 22 : if (! json_is_array (coins)) 1611 : { 1612 0 : GNUNET_break_op (0); 1613 0 : GNUNET_JSON_parse_free (spec); 1614 0 : return (MHD_YES == TALER_MHD_reply_with_error (connection, 1615 : MHD_HTTP_BAD_REQUEST, 1616 : TALER_EC_GENERIC_PARAMETER_MISSING, 1617 : "'coins' must be an array")) 1618 : ? GNUNET_NO 1619 0 : : GNUNET_SYSERR; 1620 : } 1621 : 1622 22 : pc->coins_cnt = json_array_size (coins); 1623 22 : if (pc->coins_cnt > MAX_COIN_ALLOWED_COINS) 1624 : { 1625 0 : GNUNET_break_op (0); 1626 0 : GNUNET_JSON_parse_free (spec); 1627 0 : return (MHD_YES == TALER_MHD_reply_with_error (connection, 1628 : MHD_HTTP_BAD_REQUEST, 1629 : TALER_EC_GENERIC_PARAMETER_MALFORMED, 1630 : "'coins' array too long")) 1631 : ? GNUNET_NO 1632 0 : : GNUNET_SYSERR; 1633 : } 1634 : 1635 : /* note: 1 coin = 1 deposit confirmation expected */ 1636 22 : pc->dc = GNUNET_new_array (pc->coins_cnt, 1637 : struct DepositConfirmation); 1638 : 1639 : /* This loop populates the array 'dc' in 'pc' */ 1640 : { 1641 : unsigned int coins_index; 1642 : json_t *coin; 1643 46 : json_array_foreach (coins, coins_index, coin) 1644 : { 1645 24 : struct DepositConfirmation *dc = &pc->dc[coins_index]; 1646 : const char *exchange_url; 1647 : struct GNUNET_JSON_Specification ispec[] = { 1648 24 : GNUNET_JSON_spec_fixed_auto ("coin_sig", 1649 : &dc->coin_sig), 1650 24 : GNUNET_JSON_spec_fixed_auto ("coin_pub", 1651 : &dc->coin_pub), 1652 24 : TALER_JSON_spec_denomination_signature ("ub_sig", 1653 : &dc->ub_sig), 1654 24 : GNUNET_JSON_spec_fixed_auto ("h_denom", 1655 : &dc->h_denom), 1656 24 : TALER_JSON_spec_amount ("contribution", 1657 : TMH_currency, 1658 : &dc->amount_with_fee), 1659 24 : GNUNET_JSON_spec_string ("exchange_url", 1660 : &exchange_url), 1661 24 : GNUNET_JSON_spec_end () 1662 : }; 1663 : enum GNUNET_GenericReturnValue res; 1664 : 1665 24 : res = TALER_MHD_parse_json_data (connection, 1666 : coin, 1667 : ispec); 1668 24 : if (GNUNET_YES != res) 1669 : { 1670 0 : GNUNET_break_op (0); 1671 0 : GNUNET_JSON_parse_free (spec); 1672 0 : return res; 1673 : } 1674 26 : for (unsigned int j = 0; jcoin_pub, 1678 : &pc->dc[j].coin_pub)) 1679 : { 1680 0 : GNUNET_break_op (0); 1681 : return (MHD_YES == 1682 0 : TALER_MHD_reply_with_error (connection, 1683 : MHD_HTTP_BAD_REQUEST, 1684 : TALER_EC_GENERIC_PARAMETER_MALFORMED, 1685 : "duplicate coin in list")) 1686 : ? GNUNET_NO 1687 0 : : GNUNET_SYSERR; 1688 : } 1689 : } 1690 24 : dc->exchange_url = GNUNET_strdup (exchange_url); 1691 24 : dc->index = coins_index; 1692 24 : dc->pc = pc; 1693 : 1694 24 : if (0 != 1695 24 : strcasecmp (dc->amount_with_fee.currency, 1696 : TMH_currency)) 1697 : { 1698 0 : GNUNET_break_op (0); 1699 0 : GNUNET_JSON_parse_free (spec); 1700 0 : return TALER_MHD_reply_with_error (connection, 1701 : MHD_HTTP_CONFLICT, 1702 : TALER_EC_GENERIC_CURRENCY_MISMATCH, 1703 : TMH_currency); 1704 : } 1705 : } 1706 : } 1707 22 : GNUNET_JSON_parse_free (spec); 1708 : } 1709 : 1710 : /* obtain contract terms */ 1711 : { 1712 : enum GNUNET_DB_QueryStatus qs; 1713 22 : json_t *contract_terms = NULL; 1714 : 1715 22 : qs = TMH_db->lookup_contract_terms (TMH_db->cls, 1716 22 : hc->instance->settings.id, 1717 : pc->order_id, 1718 : &contract_terms, 1719 : &pc->order_serial, 1720 : NULL); 1721 22 : if (0 > qs) 1722 : { 1723 : /* single, read-only SQL statements should never cause 1724 : serialization problems */ 1725 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); 1726 : /* Always report on hard error to enable diagnostics */ 1727 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); 1728 : return (MHD_YES == 1729 0 : TALER_MHD_reply_with_error (connection, 1730 : MHD_HTTP_INTERNAL_SERVER_ERROR, 1731 : TALER_EC_GENERIC_DB_FETCH_FAILED, 1732 : "contract terms")) 1733 : ? GNUNET_NO 1734 0 : : GNUNET_SYSERR; 1735 : } 1736 22 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 1737 : { 1738 : return (MHD_YES == 1739 0 : TALER_MHD_reply_with_error (connection, 1740 : MHD_HTTP_NOT_FOUND, 1741 : TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN, 1742 : pc->order_id)) 1743 : ? GNUNET_NO 1744 0 : : GNUNET_SYSERR; 1745 : } 1746 : 1747 : /* hash contract (needed later) */ 1748 22 : if (GNUNET_OK != 1749 22 : TALER_JSON_contract_hash (contract_terms, 1750 : &pc->h_contract_terms)) 1751 : { 1752 0 : GNUNET_break (0); 1753 0 : json_decref (contract_terms); 1754 : return (MHD_YES == 1755 0 : TALER_MHD_reply_with_error (connection, 1756 : MHD_HTTP_INTERNAL_SERVER_ERROR, 1757 : TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH, 1758 : NULL)) 1759 : ? GNUNET_NO 1760 0 : : GNUNET_SYSERR; 1761 : } 1762 22 : GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1763 : "Handling payment for order %s' with contract hash %s'\n", 1764 : pc->order_id, 1765 : GNUNET_h2s (&pc->h_contract_terms)); 1766 : 1767 : /* basic sanity check on the contract */ 1768 22 : if (NULL == json_object_get (contract_terms, 1769 : "merchant")) 1770 : { 1771 : /* invalid contract */ 1772 0 : GNUNET_break (0); 1773 0 : json_decref (contract_terms); 1774 : return (MHD_YES == 1775 0 : TALER_MHD_reply_with_error (connection, 1776 : MHD_HTTP_INTERNAL_SERVER_ERROR, 1777 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_MERCHANT_FIELD_MISSING, 1778 : NULL)) 1779 : ? GNUNET_NO 1780 0 : : GNUNET_SYSERR; 1781 : } 1782 : 1783 : /* Get details from contract and check fundamentals */ 1784 : { 1785 22 : const char *fulfillment_url = NULL; 1786 : struct GNUNET_JSON_Specification espec[] = { 1787 22 : TALER_JSON_spec_amount ("amount", 1788 : TMH_currency, 1789 : &pc->amount), 1790 22 : GNUNET_JSON_spec_mark_optional ( 1791 : GNUNET_JSON_spec_string ("fulfillment_url", 1792 : &fulfillment_url)), 1793 22 : TALER_JSON_spec_amount ("max_fee", 1794 : TMH_currency, 1795 : &pc->max_fee), 1796 22 : TALER_JSON_spec_amount ("max_wire_fee", 1797 : TMH_currency, 1798 : &pc->max_wire_fee), 1799 22 : GNUNET_JSON_spec_uint32 ("wire_fee_amortization", 1800 : &pc->wire_fee_amortization), 1801 22 : TALER_JSON_spec_absolute_time ("timestamp", 1802 : &pc->timestamp), 1803 22 : TALER_JSON_spec_absolute_time ("refund_deadline", 1804 : &pc->refund_deadline), 1805 22 : TALER_JSON_spec_absolute_time ("pay_deadline", 1806 : &pc->pay_deadline), 1807 22 : TALER_JSON_spec_absolute_time ("wire_transfer_deadline", 1808 : &pc->wire_transfer_deadline), 1809 22 : GNUNET_JSON_spec_fixed_auto ("h_wire", 1810 : &pc->h_wire), 1811 22 : GNUNET_JSON_spec_end () 1812 : }; 1813 : enum GNUNET_GenericReturnValue res; 1814 : 1815 22 : res = TALER_MHD_parse_internal_json_data (connection, 1816 : contract_terms, 1817 : espec); 1818 22 : if (NULL != fulfillment_url) 1819 21 : pc->fulfillment_url = GNUNET_strdup (fulfillment_url); 1820 22 : json_decref (contract_terms); 1821 22 : if (GNUNET_YES != res) 1822 : { 1823 0 : GNUNET_break (0); 1824 0 : return res; 1825 : } 1826 : } 1827 : 1828 22 : if (pc->wire_transfer_deadline.abs_value_us < 1829 22 : pc->refund_deadline.abs_value_us) 1830 : { 1831 : /* This should already have been checked when creating the order! */ 1832 0 : GNUNET_break (0); 1833 0 : return TALER_MHD_reply_with_error (connection, 1834 : MHD_HTTP_INTERNAL_SERVER_ERROR, 1835 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_REFUND_DEADLINE_PAST_WIRE_TRANSFER_DEADLINE, 1836 : NULL); 1837 : } 1838 : 1839 22 : if (GNUNET_TIME_absolute_is_past (pc->pay_deadline)) 1840 : { 1841 : /* too late */ 1842 : return (MHD_YES == 1843 0 : TALER_MHD_reply_with_error (connection, 1844 : MHD_HTTP_GONE, 1845 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_OFFER_EXPIRED, 1846 : NULL)) 1847 : ? GNUNET_NO 1848 0 : : GNUNET_SYSERR; 1849 : } 1850 : } 1851 : 1852 : /* Make sure wire method (still) exists for this instance */ 1853 : { 1854 : struct TMH_WireMethod *wm; 1855 : 1856 22 : wm = hc->instance->wm_head; 1857 22 : while (0 != GNUNET_memcmp (&pc->h_wire, 1858 : &wm->h_wire)) 1859 0 : wm = wm->next; 1860 22 : if (NULL == wm) 1861 : { 1862 0 : GNUNET_break (0); 1863 0 : return TALER_MHD_reply_with_error (connection, 1864 : MHD_HTTP_INTERNAL_SERVER_ERROR, 1865 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_WIRE_HASH_UNKNOWN, 1866 : NULL); 1867 : } 1868 22 : pc->wm = wm; 1869 : } 1870 : 1871 22 : return GNUNET_OK; 1872 : } 1873 : 1874 : 1875 : /** 1876 : * Handle a timeout for the processing of the pay request. 1877 : * 1878 : * @param cls our struct PayContext 1879 : */ 1880 : static void 1881 0 : handle_pay_timeout (void *cls) 1882 : { 1883 0 : struct PayContext *pc = cls; 1884 : 1885 0 : pc->timeout_task = NULL; 1886 0 : GNUNET_assert (GNUNET_YES == pc->suspended); 1887 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1888 : "Resuming pay with error after timeout\n"); 1889 0 : if (NULL != pc->fo) 1890 : { 1891 0 : TMH_EXCHANGES_find_exchange_cancel (pc->fo); 1892 0 : pc->fo = NULL; 1893 : } 1894 0 : resume_pay_with_error (pc, 1895 : MHD_HTTP_GATEWAY_TIMEOUT, 1896 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT, 1897 : NULL); 1898 0 : } 1899 : 1900 : 1901 : MHD_RESULT 1902 44 : TMH_post_orders_ID_pay (const struct TMH_RequestHandler *rh, 1903 : struct MHD_Connection *connection, 1904 : struct TMH_HandlerContext *hc) 1905 : { 1906 44 : struct PayContext *pc = hc->ctx; 1907 : 1908 44 : GNUNET_assert (NULL != hc->infix); 1909 : 1910 44 : if (NULL == pc) 1911 : { 1912 22 : pc = GNUNET_new (struct PayContext); 1913 22 : GNUNET_CONTAINER_DLL_insert (pc_head, 1914 : pc_tail, 1915 : pc); 1916 22 : pc->connection = connection; 1917 22 : pc->hc = hc; 1918 22 : pc->order_id = hc->infix; 1919 22 : hc->ctx = pc; 1920 22 : hc->cc = &pay_context_cleanup; 1921 : } 1922 44 : if (GNUNET_SYSERR == pc->suspended) 1923 0 : return MHD_NO; /* during shutdown, we don't generate any more replies */ 1924 44 : GNUNET_assert (GNUNET_NO == pc->suspended); 1925 44 : if (0 != pc->response_code) 1926 : { 1927 : MHD_RESULT res; 1928 : 1929 : /* We are *done* processing the request, just queue the response (!) */ 1930 22 : if (UINT_MAX == pc->response_code) 1931 : { 1932 0 : GNUNET_break (0); 1933 0 : return MHD_NO; /* hard error */ 1934 : } 1935 22 : res = MHD_queue_response (connection, 1936 : pc->response_code, 1937 : pc->response); 1938 22 : MHD_destroy_response (pc->response); 1939 22 : pc->response = NULL; 1940 22 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1941 : "Queueing response (%u) for POST /orders/$ID/pay (%s).\n", 1942 : (unsigned int) pc->response_code, 1943 : res ? "OK" : "FAILED"); 1944 22 : return res; 1945 : } 1946 : { 1947 : enum GNUNET_GenericReturnValue ret; 1948 : 1949 22 : ret = parse_pay (connection, 1950 : hc, 1951 : pc); 1952 22 : if (GNUNET_OK != ret) 1953 : return (GNUNET_NO == ret) 1954 : ? MHD_YES 1955 0 : : MHD_NO; 1956 : } 1957 : 1958 : /* Payment not finished, suspend while we interact with the exchange */ 1959 22 : MHD_suspend_connection (connection); 1960 22 : pc->suspended = GNUNET_YES; 1961 22 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1962 : "Suspending pay handling while working with the exchange\n"); 1963 22 : GNUNET_assert (NULL == pc->timeout_task); 1964 22 : pc->timeout_task = 1965 22 : GNUNET_SCHEDULER_add_delayed (get_pay_timeout (pc->coins_cnt), 1966 : &handle_pay_timeout, 1967 : pc); 1968 22 : execute_pay_transaction (pc); 1969 22 : return MHD_YES; 1970 : } 1971 : 1972 : 1973 : /* end of taler-merchant-httpd_post-orders-ID-pay.c */ `

 Generated by: LCOV version 1.14