taler_merchant_pay_service.c (32423B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-2024 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero General Public License as published by the Free 7 Software 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 Lesser General Public License for more details. 12 13 You should have received a copy of the GNU Lesser General Public License along with 14 TALER; see the file COPYING.LIB. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file taler_merchant_pay_service.c 18 * @brief Implementation of the the ideology 19 * from the pay_service as copy of 20 * merchant_api_post_order_pay.c 21 * @author Bohdan Potuzhnyi 22 */ 23 #include "platform.h" 24 #include <curl/curl.h> 25 #include <gnunet/gnunet_common.h> 26 #include <gnunet/gnunet_json_lib.h> 27 #include <jansson.h> 28 #include <microhttpd.h> 29 #include <gnunet/gnunet_util_lib.h> 30 #include <gnunet/gnunet_curl_lib.h> 31 #include "taler_merchant_service.h" 32 #include "taler_merchant_pay_service.h" 33 #include "merchant_api_common.h" 34 #include "merchant_api_curl_defaults.h" 35 #include <stdio.h> 36 #include <taler/taler_json_lib.h> 37 #include <taler/taler_signatures.h> 38 #include <taler/taler_exchange_service.h> 39 #include <taler/taler_curl_lib.h> 40 41 /** 42 * @brief A Pay Handle 43 */ 44 struct TALER_MERCHANT_OrderPayHandle 45 { 46 /** 47 * Reference to the GNUNET CURL execution context. 48 */ 49 struct GNUNET_CURL_Context *ctx; 50 51 /** 52 * Callback to invoke with the payment result ("pay" mode). 53 */ 54 TALER_MERCHANT_OrderPayCallback cb; 55 56 /** 57 * Closure data for @a cb. 58 */ 59 TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE *cb_cls; 60 61 /* Mandatory scalars: */ 62 63 /** 64 * Base URL of the merchant service. 65 */ 66 char *merchant_url; 67 68 /** 69 * Identifier of the order being paid. 70 */ 71 char *order_id; 72 73 /** 74 * Session identifier for this payment attempt. 75 */ 76 char *session_id; 77 78 /** 79 * Timestamp when the payment request was created. 80 */ 81 struct GNUNET_TIME_Timestamp timestamp; 82 83 /** 84 * Deadline after which refunds are no longer allowed. 85 */ 86 struct GNUNET_TIME_Timestamp refund_deadline; 87 88 /** 89 * Wire hash for communicating payment details. 90 */ 91 struct TALER_MerchantWireHashP h_wire; 92 93 /** 94 * Indicates whether @a h_wire has been set. 95 */ 96 bool has_h_wire; 97 98 /* Wallet mode fields: */ 99 100 /** 101 * Indicates whether a contract hash was provided. 102 */ 103 bool has_h_contract; 104 105 /** 106 * Hash of the private contract terms (wallet mode only). 107 */ 108 struct TALER_PrivateContractHashP h_contract_terms; 109 110 /** 111 * Indicates whether the merchant public key was provided. 112 */ 113 bool has_merchant_pub; 114 115 /** 116 * Merchant’s public key for verifying signatures (wallet mode). 117 */ 118 struct TALER_MerchantPublicKeyP merchant_pub; 119 120 /** 121 * Indicates whether a choice index was provided. 122 */ 123 bool has_choice_index; 124 125 /** 126 * Selected index of the contract choice (for token operations). 127 */ 128 int choice_index; 129 130 /** 131 * Legacy: pointer to the amount structure for strcmp checks. 132 */ 133 const struct TALER_Amount *amount; 134 135 /** 136 * Legacy: pointer to the maximum fee structure for strcmp checks. 137 */ 138 const struct TALER_Amount *max_fee; 139 140 /* Raw arrays as passed in via set_options(): */ 141 142 /** 143 * Coins used for payment. 144 */ 145 struct 146 { 147 /** 148 * Number of coins provided. 149 */ 150 unsigned int num_coins; 151 /** 152 * Array of coins to spend. 153 */ 154 const struct TALER_MERCHANT_PayCoin *coins; 155 } coins; 156 157 /** 158 * Input tokens to use (wallet mode). 159 */ 160 struct 161 { 162 /** 163 * Number of tokens provided. 164 */ 165 unsigned int num_tokens; 166 /** 167 * Array of tokens to redeem. 168 */ 169 const struct TALER_MERCHANT_UseToken *tokens; 170 } input_tokens; 171 172 /** 173 * Output tokens expected from the merchant. 174 */ 175 struct 176 { 177 /** 178 * Number of output tokens expected. 179 */ 180 unsigned int num_output_tokens; 181 /** 182 * Array of expected output tokens. 183 */ 184 const struct TALER_MERCHANT_OutputToken *output_tokens; 185 } output_tokens; 186 187 /** 188 * JSON array of token envelope events (from Donau). 189 */ 190 json_t *tokens_evs; 191 192 /* Computed once both choice_index and tokens_evs are available: */ 193 194 /** 195 * JSON object containing wallet-specific data payload. 196 */ 197 json_t *wallet_data; 198 199 /** 200 * Hash code of @a wallet_data for integrity checks. 201 */ 202 struct GNUNET_HashCode wallet_data_hash; 203 204 /** 205 * JSON body being constructed for the HTTP POST. 206 */ 207 json_t *body; 208 209 /* Final URL and CURL plumbing: */ 210 211 /** 212 * Fully formed URL for the POST /order/$ID/pay request. 213 */ 214 char *url; 215 216 /** 217 * CURL post context managing headers and body. 218 */ 219 struct TALER_CURL_PostContext post_ctx; 220 221 /** 222 * Handle for the asynchronous CURL job. 223 */ 224 struct GNUNET_CURL_Job *job; 225 226 /** 227 * Flags indicating which payment options have been set. 228 */ 229 bool field_seen[TALER_MERCHANT_OrderPayOptionType_LENGTH]; 230 231 /** 232 * True if operating in wallet mode (using tokens/contracts). 233 */ 234 bool am_wallet; 235 236 /** 237 * Raw JSON data of `donau` for `wallet_data`. 238 */ 239 json_t *donau_data; 240 }; 241 242 /** 243 * Parse blindly signed output tokens from response. 244 * 245 * @param token_sigs the JSON array with the token signatures. Can be NULL. 246 * @param tokens where to store the parsed tokens. 247 * @param num_tokens where to store the length of the @a tokens array. 248 */ 249 static enum GNUNET_GenericReturnValue 250 parse_tokens (const json_t *token_sigs, 251 struct TALER_MERCHANT_OutputToken **tokens, 252 unsigned int *num_tokens) 253 { 254 GNUNET_array_grow (*tokens, 255 *num_tokens, 256 json_array_size (token_sigs)); 257 258 for (unsigned int i = 0; i<(*num_tokens); i++) 259 { 260 struct TALER_MERCHANT_OutputToken *token = &(*tokens)[i]; 261 struct GNUNET_JSON_Specification spec[] = { 262 TALER_JSON_spec_blinded_token_issue_sig ("blind_sig", 263 &token->blinded_sig), 264 GNUNET_JSON_spec_end () 265 }; 266 const json_t *jtoken 267 = json_array_get (token_sigs, 268 i); 269 270 if (NULL == jtoken) 271 { 272 GNUNET_break (0); 273 return GNUNET_SYSERR; 274 } 275 if (GNUNET_OK != 276 GNUNET_JSON_parse (jtoken, 277 spec, 278 NULL, NULL)) 279 { 280 GNUNET_break (0); 281 return GNUNET_SYSERR; 282 } 283 } 284 285 return GNUNET_YES; 286 } 287 288 289 /** 290 * Function called when we're done processing the 291 * HTTP /pay request. 292 * 293 * @param cls the `struct TALER_MERCHANT_Pay` 294 * @param response_code HTTP response code, 0 on error 295 * @param resp response body, NULL if not in JSON 296 */ 297 static void 298 handle_finished (void *cls, 299 long response_code, 300 const void *resp) 301 { 302 struct TALER_MERCHANT_OrderPayHandle *oph = cls; 303 const json_t *json = resp; 304 struct TALER_MERCHANT_PayResponse pr = { 305 .hr.http_status = (unsigned int) response_code, 306 .hr.reply = json 307 }; 308 309 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 310 "Received /pay response with status code %u\n", 311 (unsigned int) response_code); 312 313 json_dumpf (json, 314 stderr, 315 JSON_INDENT (2)); 316 317 oph->job = NULL; 318 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 319 "/pay completed with response code %u\n", 320 (unsigned int) response_code); 321 switch (response_code) 322 { 323 case 0: 324 pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 325 break; 326 case MHD_HTTP_OK: 327 if (oph->am_wallet) 328 { 329 const json_t *token_sigs = NULL; 330 struct GNUNET_JSON_Specification spec[] = { 331 GNUNET_JSON_spec_fixed_auto ("sig", 332 &pr.details.ok.merchant_sig), 333 GNUNET_JSON_spec_mark_optional ( 334 GNUNET_JSON_spec_string ("pos_confirmation", 335 &pr.details.ok.pos_confirmation), 336 NULL), 337 GNUNET_JSON_spec_mark_optional ( 338 GNUNET_JSON_spec_array_const ("token_sigs", 339 &token_sigs), 340 NULL), 341 GNUNET_JSON_spec_end () 342 }; 343 344 if (GNUNET_OK != 345 GNUNET_JSON_parse (json, 346 spec, 347 NULL, NULL)) 348 { 349 GNUNET_break_op (0); 350 pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 351 pr.hr.http_status = 0; 352 pr.hr.hint = "sig field missing in response"; 353 break; 354 } 355 356 if (GNUNET_OK != 357 parse_tokens (token_sigs, 358 &pr.details.ok.tokens, 359 &pr.details.ok.num_tokens)) 360 { 361 GNUNET_break_op (0); 362 pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 363 pr.hr.http_status = 0; 364 pr.hr.hint = "failed to parse token_sigs field in response"; 365 break; 366 } 367 368 if (GNUNET_OK != 369 TALER_merchant_pay_verify (&oph->h_contract_terms, 370 &oph->merchant_pub, 371 &pr.details.ok.merchant_sig)) 372 { 373 GNUNET_break_op (0); 374 pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 375 pr.hr.http_status = 0; 376 pr.hr.hint = "signature invalid"; 377 } 378 } 379 break; 380 /* Tolerating Not Acceptable because sometimes 381 * - especially in tests - we might want to POST 382 * coins one at a time. */ 383 case MHD_HTTP_NOT_ACCEPTABLE: 384 pr.hr.ec = TALER_JSON_get_error_code (json); 385 pr.hr.hint = TALER_JSON_get_error_hint (json); 386 break; 387 case MHD_HTTP_BAD_REQUEST: 388 pr.hr.ec = TALER_JSON_get_error_code (json); 389 pr.hr.hint = TALER_JSON_get_error_hint (json); 390 /* This should never happen, either us 391 * or the merchant is buggy (or API version conflict); 392 * just pass JSON reply to the application */ 393 break; 394 case MHD_HTTP_PAYMENT_REQUIRED: 395 /* was originally paid, but then refunded */ 396 pr.hr.ec = TALER_JSON_get_error_code (json); 397 pr.hr.hint = TALER_JSON_get_error_hint (json); 398 break; 399 case MHD_HTTP_FORBIDDEN: 400 pr.hr.ec = TALER_JSON_get_error_code (json); 401 pr.hr.hint = TALER_JSON_get_error_hint (json); 402 break; 403 case MHD_HTTP_NOT_FOUND: 404 pr.hr.ec = TALER_JSON_get_error_code (json); 405 pr.hr.hint = TALER_JSON_get_error_hint (json); 406 /* Nothing really to verify, this should never 407 happen, we should pass the JSON reply to the 408 application */ 409 break; 410 case MHD_HTTP_REQUEST_TIMEOUT: 411 pr.hr.ec = TALER_JSON_get_error_code (json); 412 pr.hr.hint = TALER_JSON_get_error_hint (json); 413 /* The merchant couldn't generate a timely response, likely because 414 it itself waited too long on the exchange. 415 Pass on to application. */ 416 break; 417 case MHD_HTTP_CONFLICT: 418 TALER_MERCHANT_parse_error_details_ (json, 419 MHD_HTTP_CONFLICT, 420 &pr.hr); 421 break; 422 case MHD_HTTP_GONE: 423 TALER_MERCHANT_parse_error_details_ (json, 424 response_code, 425 &pr.hr); 426 /* The merchant says we are too late, the offer has expired or some 427 denomination key of a coin involved has expired. 428 Might be a disagreement in timestamps? Still, pass on to application. */ 429 break; 430 case MHD_HTTP_PRECONDITION_FAILED: 431 TALER_MERCHANT_parse_error_details_ (json, 432 response_code, 433 &pr.hr); 434 /* Nothing really to verify, the merchant is blaming us for failing to 435 satisfy some constraint (likely it does not like our exchange because 436 of some disagreement on the PKI). We should pass the JSON reply to the 437 application */ 438 break; 439 case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: 440 { 441 json_t *ebus = json_object_get (json, 442 "exchange_base_urls"); 443 if (NULL == ebus) 444 { 445 GNUNET_break_op (0); 446 pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 447 pr.hr.http_status = 0; 448 pr.hr.hint = "failed to parse exchange_base_urls field in response"; 449 break; 450 } 451 { 452 size_t alen = json_array_size (ebus); 453 const char *ebua[GNUNET_NZL (alen)]; 454 size_t idx; 455 json_t *jebu; 456 bool ok = true; 457 458 GNUNET_assert (alen <= UINT_MAX); 459 json_array_foreach (ebus, idx, jebu) 460 { 461 ebua[idx] = json_string_value (jebu); 462 if (NULL == ebua[idx]) 463 { 464 GNUNET_break_op (0); 465 pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 466 pr.hr.http_status = 0; 467 pr.hr.hint = "non-string value in exchange_base_urls in response"; 468 ok = false; 469 break; 470 } 471 } 472 if (! ok) 473 break; 474 pr.details.unavailable_for_legal_reasons.num_exchanges 475 = (unsigned int) alen; 476 pr.details.unavailable_for_legal_reasons.exchanges 477 = ebua; 478 oph->cb (oph->cb_cls, 479 &pr); 480 TALER_MERCHANT_order_pay_cancel1 (oph); 481 return; 482 } 483 } 484 break; 485 case MHD_HTTP_INTERNAL_SERVER_ERROR: 486 TALER_MERCHANT_parse_error_details_ (json, 487 response_code, 488 &pr.hr); 489 /* Server had an internal issue; we should retry, 490 but this API leaves this to the application */ 491 break; 492 case MHD_HTTP_BAD_GATEWAY: 493 /* Nothing really to verify, the merchant is blaming the exchange. 494 We should pass the JSON reply to the application */ 495 TALER_MERCHANT_parse_error_details_ (json, 496 response_code, 497 &pr.hr); 498 break; 499 case MHD_HTTP_SERVICE_UNAVAILABLE: 500 TALER_MERCHANT_parse_error_details_ (json, 501 response_code, 502 &pr.hr); 503 /* Exchange couldn't respond properly; the retry is 504 left to the application */ 505 break; 506 case MHD_HTTP_GATEWAY_TIMEOUT: 507 TALER_MERCHANT_parse_error_details_ (json, 508 response_code, 509 &pr.hr); 510 /* Exchange couldn't respond in a timely fashion; 511 the retry is left to the application */ 512 break; 513 default: 514 TALER_MERCHANT_parse_error_details_ (json, 515 response_code, 516 &pr.hr); 517 /* unexpected response code */ 518 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 519 "Unexpected response code %u/%d\n", 520 (unsigned int) response_code, 521 (int) pr.hr.ec); 522 GNUNET_break_op (0); 523 break; 524 } 525 oph->cb (oph->cb_cls, 526 &pr); 527 528 if (pr.details.ok.tokens) 529 { 530 GNUNET_free (pr.details.ok.tokens); 531 pr.details.ok.tokens = NULL; 532 pr.details.ok.num_tokens = 0; 533 } 534 535 TALER_MERCHANT_order_pay_cancel1 (oph); 536 } 537 538 539 /** 540 * @brief Create and initialize a new payment handle 541 * 542 * Allocates a TALER_MERCHANT_OrderPayHandle, sets up its context and 543 * prepares an empty JSON body for the /orders/$ID/pay request. 544 */ 545 struct TALER_MERCHANT_OrderPayHandle * 546 TALER_MERCHANT_order_pay_create (struct GNUNET_CURL_Context *ctx, 547 TALER_MERCHANT_OrderPayCallback cb, 548 TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE 549 *cb_cls) 550 { 551 struct TALER_MERCHANT_OrderPayHandle *ph = 552 GNUNET_new (struct TALER_MERCHANT_OrderPayHandle); 553 ph->ctx = ctx; 554 ph->cb = cb; 555 ph->cb_cls = cb_cls; 556 ph->body = json_object (); 557 GNUNET_assert (ph->body); 558 return ph; 559 } 560 561 562 /** 563 * @brief Cancel and free a payment handle 564 * 565 * Aborts any in-flight CURL job, releases all JSON objects and internal 566 * buffers, and frees the handle structure itself. 567 */ 568 void 569 TALER_MERCHANT_order_pay_cancel1 (struct TALER_MERCHANT_OrderPayHandle *ph) 570 { 571 if (ph->job) 572 GNUNET_CURL_job_cancel (ph->job); 573 ph->job = NULL; 574 575 TALER_curl_easy_post_finished (&ph->post_ctx); 576 577 if (ph->body) 578 json_decref (ph->body); 579 ph->body = NULL; 580 581 if (ph->wallet_data) 582 json_decref (ph->wallet_data); 583 ph->wallet_data = NULL; 584 585 if (ph->tokens_evs) 586 json_decref (ph->tokens_evs); 587 ph->tokens_evs = NULL; 588 589 if (ph->donau_data) 590 json_decref (ph->donau_data); 591 592 GNUNET_free (ph->url); 593 GNUNET_free (ph->merchant_url); 594 GNUNET_free (ph->session_id); 595 GNUNET_free (ph->order_id); 596 GNUNET_free (ph); 597 } 598 599 600 /** 601 * @brief Store a JSON snippet under a payment option key 602 * 603 * Ensures that an option of type @a ot has not already been set, 604 * then merges @a snippet into the handle's JSON @c body. Marks the 605 * field_seen flag and frees @a snippet. 606 * 607 * @param ph payment handle receiving the snippet 608 * @param ot option type under which to store @a snippet 609 * @param snippet JSON object representing the option payload 610 * @return #TALER_MERCHANT_OPOEC_OK if stored; appropriate error code otherwise 611 */ 612 static enum TALER_MERCHANT_OrderPayErrorCode 613 store_json_option (struct TALER_MERCHANT_OrderPayHandle *ph, 614 enum TALER_MERCHANT_OrderPayOptionType ot, 615 json_t *snippet) 616 { 617 if (ph->field_seen[ot]) 618 { 619 json_decref (snippet); 620 return TALER_MERCHANT_OPOEC_DUPLICATE_OPTION; 621 } 622 ph->field_seen[ot] = true; 623 GNUNET_assert (0 == json_object_update (ph->body, 624 snippet)); 625 json_decref (snippet); 626 return TALER_MERCHANT_OPOEC_OK; 627 } 628 629 630 /** 631 * @brief Apply user-supplied options to a payment handle 632 * 633 * Iterates through a NULL-terminated array of #TALER_MERCHANT_OrderPayOption 634 * entries, validates and stores each into @a ph. Handles JSON packing and 635 * internal state updates for coins, tokens, deadlines, Donau data, etc. 636 */ 637 enum TALER_MERCHANT_OrderPayErrorCode 638 TALER_MERCHANT_order_pay_set_options ( 639 struct TALER_MERCHANT_OrderPayHandle *ph, 640 const struct TALER_MERCHANT_OrderPayOption options[], 641 size_t max_options) 642 { 643 for (size_t i = 0; i < max_options 644 && options[i].ot != TALER_MERCHANT_OrderPayOptionType_END; i++) 645 { 646 const struct TALER_MERCHANT_OrderPayOption *o = &options[i]; 647 648 switch (o->ot) 649 { 650 case TALER_MERCHANT_OrderPayOptionType_MERCHANT_URL: 651 ph->merchant_url = GNUNET_strdup (o->details.merchant_url); 652 break; 653 654 case TALER_MERCHANT_OrderPayOptionType_SESSION_ID: 655 ph->session_id = GNUNET_strdup (o->details.session_id); 656 /* add straight into JSON body */ 657 { 658 json_t *js = GNUNET_JSON_PACK (GNUNET_JSON_pack_string ("session_id", 659 o->details. 660 session_id)); 661 enum TALER_MERCHANT_OrderPayErrorCode ec = 662 store_json_option (ph, 663 o->ot, 664 js); 665 if (TALER_MERCHANT_OPOEC_OK != ec) 666 return ec; 667 break; 668 } 669 670 case TALER_MERCHANT_OrderPayOptionType_ORDER_ID: 671 { 672 ph->order_id = GNUNET_strdup (o->details.order_id); 673 break; 674 } 675 676 case TALER_MERCHANT_OrderPayOptionType_H_CONTRACT: 677 { 678 ph->h_contract_terms = *o->details.h_contract; 679 ph->has_h_contract = true; 680 681 break; 682 } 683 684 case TALER_MERCHANT_OrderPayOptionType_CHOICE_INDEX: 685 ph->choice_index = o->details.choice_index; 686 ph->has_choice_index = true; 687 break; 688 689 case TALER_MERCHANT_OrderPayOptionType_AMOUNT: 690 { 691 ph->amount = &o->details.amount; 692 break; 693 } 694 695 case TALER_MERCHANT_OrderPayOptionType_MAX_FEE: 696 { 697 ph->max_fee = &o->details.max_fee; 698 break; 699 } 700 701 case TALER_MERCHANT_OrderPayOptionType_MERCHANT_PUB: 702 { 703 ph->merchant_pub = o->details.merchant_pub; 704 ph->has_merchant_pub = true; 705 706 break; 707 } 708 709 case TALER_MERCHANT_OrderPayOptionType_TIMESTAMP: 710 { 711 ph->timestamp = o->details.timestamp; 712 break; 713 } 714 715 case TALER_MERCHANT_OrderPayOptionType_REFUND_DEADLINE: 716 { 717 ph->refund_deadline = o->details.refund_deadline; 718 break; 719 } 720 721 case TALER_MERCHANT_OrderPayOptionType_PAY_DEADLINE: 722 { 723 /* FIXME: This one comes from the merchant_api_post_order_pay 724 no idea do we still need it or not? */ 725 break; 726 } 727 728 case TALER_MERCHANT_OrderPayOptionType_H_WIRE: 729 { 730 ph->h_wire = o->details.h_wire; 731 ph->has_h_wire = true; 732 break; 733 } 734 735 case TALER_MERCHANT_OrderPayOptionType_COINS: 736 /* stash for later signing */ 737 ph->coins.num_coins = o->details.coins.num_coins; 738 ph->coins.coins = o->details.coins.coins; 739 break; 740 741 case TALER_MERCHANT_OrderPayOptionType_INPUT_TOKENS: 742 /* stash for later signing */ 743 ph->input_tokens.num_tokens = o->details.input_tokens.num_tokens; 744 ph->input_tokens.tokens = o->details.input_tokens.tokens; 745 break; 746 747 case TALER_MERCHANT_OrderPayOptionType_OUTPUT_TOKENS: 748 /* store JSON array directly, *and* stash for hash */ 749 ph->output_tokens.num_output_tokens = 750 o->details.output_tokens.num_output_tokens; 751 ph->output_tokens.output_tokens = o->details.output_tokens.output_tokens; 752 { 753 /* build and store tokens_evs */ 754 json_t *arr = json_array (); 755 756 GNUNET_assert (NULL != arr); 757 for (unsigned j = 0; j < ph->output_tokens.num_output_tokens; j++) 758 { 759 const struct TALER_MERCHANT_OutputToken *otk = 760 &ph->output_tokens.output_tokens[j]; 761 json_t *je = GNUNET_JSON_PACK (TALER_JSON_pack_token_envelope (NULL, 762 &otk-> 763 envelope)); 764 GNUNET_assert (0 == 765 json_array_append_new (arr, 766 je)); 767 } 768 769 ph->tokens_evs = arr; 770 771 ph->field_seen[o->ot] = true; 772 } 773 break; 774 775 case TALER_MERCHANT_OrderPayOptionType_DONAU_URL: 776 if (NULL == ph->donau_data) 777 ph->donau_data = json_object (); 778 GNUNET_assert (0 == 779 json_object_set_new ( 780 ph->donau_data, 781 "url", 782 json_string (o->details.donau_url))); 783 break; 784 785 case TALER_MERCHANT_OrderPayOptionType_DONAU_YEAR: 786 { 787 if (ph->donau_data == NULL) 788 ph->donau_data = json_object (); 789 GNUNET_assert (0 == json_object_set_new ( 790 ph->donau_data, 791 "year", 792 json_integer ((json_int_t) o->details.donau_year))); 793 break; 794 } 795 796 case TALER_MERCHANT_OrderPayOptionType_DONAU_BUDIS: 797 { 798 if (ph->donau_data == NULL) 799 ph->donau_data = json_object (); 800 GNUNET_assert (0 == json_object_set_new ( 801 ph->donau_data, 802 "budikeypairs", 803 json_incref (o->details.donau_budis_json))); 804 break; 805 } 806 807 default: 808 return TALER_MERCHANT_OPOEC_UNKNOWN_OPTION; 809 } 810 } 811 return TALER_MERCHANT_OPOEC_OK; 812 } 813 814 815 /** 816 * @brief Dispatch the /orders/$ID/pay request 817 * 818 * Validates that all mandatory parameters (merchant_url, order_id, coins) 819 * have been set, builds the final JSON payload, constructs the URL, 820 * and issues an asynchronous HTTP POST. The payment handle's callback 821 * will receive completion notifications. 822 */ 823 enum TALER_MERCHANT_OrderPayErrorCode 824 TALER_MERCHANT_order_pay_start (struct TALER_MERCHANT_OrderPayHandle *ph) 825 { 826 /* all the old mandatory checks */ 827 if ( (! ph->merchant_url) || 828 (! ph->order_id) ) 829 { 830 GNUNET_break (0); 831 return TALER_MERCHANT_OPOEC_MISSING_MANDATORY; 832 } 833 if (GNUNET_YES != 834 TALER_amount_cmp_currency (ph->amount, 835 ph->max_fee)) 836 { 837 GNUNET_break (0); 838 return TALER_MERCHANT_OPOEC_INVALID_VALUE; 839 } 840 841 /* build wallet_data hash for signing coins & tokens */ 842 if (ph->has_choice_index) 843 { 844 /* base fields */ 845 json_t *wd = GNUNET_JSON_PACK ( 846 GNUNET_JSON_pack_int64 ("choice_index", 847 ph->choice_index), 848 GNUNET_JSON_pack_allow_null ( 849 GNUNET_JSON_pack_array_incref ("tokens_evs", 850 ph->tokens_evs)) 851 ); 852 853 /* Putting prepared donau_data into the wallet_data */ 854 if (ph->donau_data) 855 GNUNET_assert (0 == json_object_set_new ( 856 wd, 857 "donau", 858 json_incref (ph->donau_data))); 859 860 ph->wallet_data = wd; 861 862 TALER_json_hash (ph->wallet_data, 863 &ph->wallet_data_hash); 864 865 store_json_option (ph, 866 TALER_MERCHANT_OrderPayOptionType_WALLET_DATA, 867 GNUNET_JSON_PACK ( 868 GNUNET_JSON_pack_object_incref ("wallet_data", 869 ph->wallet_data))); 870 } 871 872 /* sign coins AND build the “coins” JSON in one pass */ 873 { 874 struct TALER_Amount total_fee; 875 struct TALER_Amount total_amount; 876 json_t *arr = json_array (); 877 878 GNUNET_assert (NULL != arr); 879 for (unsigned i = 0; i < ph->coins.num_coins; i++) 880 { 881 const struct TALER_MERCHANT_PayCoin *c = &ph->coins.coins[i]; 882 struct TALER_MERCHANT_PaidCoin pc; 883 json_t *je; 884 885 /* sign */ 886 struct TALER_Amount fee; 887 struct TALER_DenominationHashP h_denom_pub; 888 889 TALER_denom_pub_hash (&c->denom_pub, 890 &h_denom_pub); 891 if (0 > TALER_amount_subtract (&fee, 892 &c->amount_with_fee, 893 &c->amount_without_fee)) 894 return TALER_MERCHANT_OPOEC_INVALID_VALUE; 895 896 897 TALER_wallet_deposit_sign (&c->amount_with_fee, 898 &fee, 899 &ph->h_wire, 900 &ph->h_contract_terms, 901 (NULL != ph->wallet_data) 902 ? &ph->wallet_data_hash 903 : NULL, 904 c->h_age_commitment, 905 NULL, 906 &h_denom_pub, 907 ph->timestamp, 908 &ph->merchant_pub, 909 ph->refund_deadline, 910 &c->coin_priv, 911 &pc.coin_sig); 912 913 pc.denom_pub = c->denom_pub; 914 pc.denom_sig = c->denom_sig; 915 pc.denom_value = c->denom_value; 916 pc.amount_with_fee = c->amount_with_fee; 917 pc.amount_without_fee = c->amount_without_fee; 918 pc.exchange_url = c->exchange_url; 919 GNUNET_CRYPTO_eddsa_key_get_public (&c->coin_priv.eddsa_priv, 920 &pc.coin_pub.eddsa_pub); 921 922 /* JSON */ 923 je = GNUNET_JSON_PACK (TALER_JSON_pack_amount ("contribution", 924 &pc.amount_with_fee), 925 GNUNET_JSON_pack_data_auto ("coin_pub", 926 &pc.coin_pub), 927 GNUNET_JSON_pack_string ("exchange_url", 928 pc.exchange_url), 929 GNUNET_JSON_pack_data_auto ("h_denom", 930 &h_denom_pub), 931 TALER_JSON_pack_denom_sig ("ub_sig", 932 &pc.denom_sig), 933 GNUNET_JSON_pack_data_auto ("coin_sig", 934 &pc.coin_sig)); 935 GNUNET_assert (0 == 936 json_array_append_new (arr, 937 je)); 938 939 /* optional totals if you need them later 940 (kept here because they existed in the legacy code) */ 941 if (0 == i) 942 { 943 total_fee = fee; 944 total_amount = pc.amount_with_fee; 945 } 946 else 947 { 948 if ( (0 > 949 TALER_amount_add (&total_fee, 950 &total_fee, 951 &fee)) || 952 (0 > 953 TALER_amount_add (&total_amount, 954 &total_amount, 955 &pc.amount_with_fee)) ) 956 { 957 return TALER_MERCHANT_OPOEC_INVALID_VALUE; 958 } 959 } 960 } 961 962 /* Putting coins to the body*/ 963 { 964 enum TALER_MERCHANT_OrderPayErrorCode ec = 965 store_json_option (ph, 966 TALER_MERCHANT_OrderPayOptionType_COINS, 967 GNUNET_JSON_PACK ( 968 GNUNET_JSON_pack_array_steal ("coins", 969 arr) 970 )); 971 if (TALER_MERCHANT_OPOEC_OK != ec) 972 { 973 return ec; 974 } 975 } 976 } 977 978 /* sign & pack input_tokens into used_tokens array in body */ 979 if (ph->input_tokens.num_tokens > 0) 980 { 981 struct TALER_MERCHANT_UsedToken ut[ph->input_tokens.num_tokens]; 982 json_t *arr = json_array (); 983 984 GNUNET_assert (NULL != arr); 985 for (unsigned i = 0; i < ph->input_tokens.num_tokens; i++) 986 { 987 json_t *je; 988 const struct TALER_MERCHANT_UseToken *in = &ph->input_tokens.tokens[i]; 989 struct TALER_MERCHANT_UsedToken *t = &ut[i]; 990 991 TALER_wallet_token_use_sign (&ph->h_contract_terms, 992 &ph->wallet_data_hash, 993 &in->token_priv, 994 &t->token_sig); 995 996 t->ub_sig = in->ub_sig; 997 t->issue_pub = in->issue_pub; 998 999 GNUNET_CRYPTO_eddsa_key_get_public (&in->token_priv.private_key, 1000 &t->token_pub.public_key); 1001 1002 je = GNUNET_JSON_PACK ( 1003 GNUNET_JSON_pack_data_auto ("token_sig", 1004 &t->token_sig), 1005 TALER_JSON_pack_token_issue_sig ("ub_sig", 1006 &t->ub_sig), 1007 GNUNET_JSON_pack_data_auto ("h_issue", 1008 &t->issue_pub.public_key->pub_key_hash), 1009 GNUNET_JSON_pack_data_auto ("token_pub", 1010 &t->token_pub) 1011 ); 1012 GNUNET_assert (0 == 1013 json_array_append_new (arr, 1014 je)); 1015 } 1016 1017 store_json_option (ph, 1018 TALER_MERCHANT_OrderPayOptionType_INPUT_TOKENS, 1019 GNUNET_JSON_PACK ( 1020 GNUNET_JSON_pack_array_steal ("tokens", 1021 arr) 1022 ) 1023 ); 1024 } 1025 1026 1027 /* post the request */ 1028 { 1029 char *path; 1030 CURL *eh; 1031 GNUNET_asprintf (&path, 1032 "orders/%s/pay", 1033 ph->order_id); 1034 ph->url = TALER_url_join (ph->merchant_url, 1035 path, 1036 NULL); 1037 GNUNET_free (path); 1038 1039 if (NULL == ph->url) 1040 { 1041 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1042 "Could not construct request URL.\n"); 1043 json_decref (ph->body); 1044 GNUNET_free (ph); 1045 return TALER_MERCHANT_OPOEC_URL_FAILURE; 1046 } 1047 1048 eh = TALER_MERCHANT_curl_easy_get_ (ph->url); 1049 if (GNUNET_OK != 1050 TALER_curl_easy_post (&ph->post_ctx, 1051 eh, 1052 ph->body)) 1053 { 1054 GNUNET_break (0); 1055 curl_easy_cleanup (eh); 1056 GNUNET_free (ph->url); 1057 GNUNET_free (ph); 1058 return TALER_MERCHANT_OPOEC_CURL_FAILURE; 1059 } 1060 1061 ph->job = GNUNET_CURL_job_add2 (ph->ctx, 1062 eh, 1063 ph->post_ctx.headers, 1064 &handle_finished, 1065 ph); 1066 1067 ph->am_wallet = true; 1068 return TALER_MERCHANT_OPOEC_OK; 1069 } 1070 }