merchant_api_post_order_pay.c (25010B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-2023 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify 6 it under the terms of the GNU Lesser General Public License as 7 published by the Free Software Foundation; either version 2.1, 8 or (at your option) any later version. 9 10 TALER is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General 16 Public License along with TALER; see the file COPYING.LGPL. 17 If not, see <http://www.gnu.org/licenses/> 18 */ 19 /** 20 * @file merchant_api_post_order_pay.c 21 * @brief Implementation of the POST /order/$ID/pay request 22 * of the merchant's HTTP API 23 * @author Christian Grothoff 24 * @author Marcello Stanisci 25 */ 26 #include "platform.h" 27 #include <curl/curl.h> 28 #include <gnunet/gnunet_common.h> 29 #include <gnunet/gnunet_json_lib.h> 30 #include <jansson.h> 31 #include <microhttpd.h> /* just for HTTP status codes */ 32 #include <gnunet/gnunet_util_lib.h> 33 #include <gnunet/gnunet_curl_lib.h> 34 #include "taler_merchant_service.h" 35 #include "merchant_api_common.h" 36 #include "merchant_api_curl_defaults.h" 37 #include <stdio.h> 38 #include <taler/taler_json_lib.h> 39 #include <taler/taler_signatures.h> 40 #include <taler/taler_exchange_service.h> 41 #include <taler/taler_curl_lib.h> 42 43 44 /** 45 * @brief A Pay Handle 46 */ 47 struct TALER_MERCHANT_OrderPayHandle 48 { 49 50 /** 51 * The url for this request. 52 */ 53 char *url; 54 55 /** 56 * Handle for the request. 57 */ 58 struct GNUNET_CURL_Job *job; 59 60 /** 61 * Function to call with the result in "pay" @e mode. 62 */ 63 TALER_MERCHANT_OrderPayCallback pay_cb; 64 65 /** 66 * Closure for @a pay_cb. 67 */ 68 void *pay_cb_cls; 69 70 /** 71 * Reference to the execution context. 72 */ 73 struct GNUNET_CURL_Context *ctx; 74 75 /** 76 * Minor context that holds body and headers. 77 */ 78 struct TALER_CURL_PostContext post_ctx; 79 80 /** 81 * The coins we are paying with. 82 */ 83 struct TALER_MERCHANT_PaidCoin *coins; 84 85 /** 86 * Hash of the contract we are paying, set 87 * if @e am_wallet is true. 88 */ 89 struct TALER_PrivateContractHashP h_contract_terms; 90 91 /** 92 * Public key of the merchant (instance) being paid, set 93 * if @e am_wallet is true. 94 */ 95 struct TALER_MerchantPublicKeyP merchant_pub; 96 97 /** 98 * JSON with the full reply, used during async 99 * processing. 100 */ 101 json_t *full_reply; 102 103 /** 104 * Pointer into @e coins array for the coin that 105 * created a conflict (that we are checking). 106 */ 107 const struct TALER_MERCHANT_PaidCoin *error_pc; 108 109 /** 110 * Coin history that proves a conflict. 111 */ 112 json_t *error_history; 113 114 /** 115 * Number of @e coins we are paying with. 116 */ 117 unsigned int num_coins; 118 119 /** 120 * Set to true if this is the wallet API and we have 121 * initialized @e h_contract_terms and @e merchant_pub. 122 */ 123 bool am_wallet; 124 125 }; 126 127 128 /** 129 * Parse blindly signed output tokens from response. 130 * 131 * @param token_sigs the JSON array with the token signatures. Can be NULL. 132 * @param tokens where to store the parsed tokens. 133 * @param num_tokens where to store the length of the @a tokens array. 134 */ 135 static enum GNUNET_GenericReturnValue 136 parse_tokens (const json_t *token_sigs, 137 struct TALER_MERCHANT_OutputToken **tokens, 138 unsigned int *num_tokens) 139 { 140 GNUNET_array_grow (*tokens, 141 *num_tokens, 142 json_array_size (token_sigs)); 143 144 for (unsigned int i = 0; i<(*num_tokens); i++) 145 { 146 struct TALER_MERCHANT_OutputToken *token = &(*tokens)[i]; 147 struct GNUNET_JSON_Specification spec[] = { 148 TALER_JSON_spec_blinded_token_issue_sig ("blind_sig", 149 &token->blinded_sig), 150 GNUNET_JSON_spec_end () 151 }; 152 const json_t *jtoken 153 = json_array_get (token_sigs, 154 i); 155 156 if (NULL == jtoken) 157 { 158 GNUNET_break (0); 159 return GNUNET_SYSERR; 160 } 161 if (GNUNET_OK != 162 GNUNET_JSON_parse (jtoken, 163 spec, 164 NULL, NULL)) 165 { 166 GNUNET_break (0); 167 return GNUNET_SYSERR; 168 } 169 } 170 171 return GNUNET_YES; 172 } 173 174 175 /** 176 * Function called when we're done processing the 177 * HTTP /pay request. 178 * 179 * @param cls the `struct TALER_MERCHANT_Pay` 180 * @param response_code HTTP response code, 0 on error 181 * @param response response body, NULL if not in JSON 182 */ 183 static void 184 handle_pay_finished (void *cls, 185 long response_code, 186 const void *response) 187 { 188 struct TALER_MERCHANT_OrderPayHandle *oph = cls; 189 const json_t *json = response; 190 struct TALER_MERCHANT_PayResponse pr = { 191 .hr.http_status = (unsigned int) response_code, 192 .hr.reply = json 193 }; 194 195 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 196 "Received /pay response with status code %u\n", 197 (unsigned int) response_code); 198 199 json_dumpf (json, 200 stderr, 201 JSON_INDENT (2)); 202 203 oph->job = NULL; 204 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 205 "/pay completed with response code %u\n", 206 (unsigned int) response_code); 207 switch (response_code) 208 { 209 case 0: 210 pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 211 break; 212 case MHD_HTTP_OK: 213 if (oph->am_wallet) 214 { 215 const json_t *token_sigs = NULL; 216 struct GNUNET_JSON_Specification spec[] = { 217 GNUNET_JSON_spec_fixed_auto ("sig", 218 &pr.details.ok.merchant_sig), 219 GNUNET_JSON_spec_mark_optional ( 220 GNUNET_JSON_spec_string ("pos_confirmation", 221 &pr.details.ok.pos_confirmation), 222 NULL), 223 GNUNET_JSON_spec_mark_optional ( 224 GNUNET_JSON_spec_array_const ("token_sigs", 225 &token_sigs), 226 NULL), 227 GNUNET_JSON_spec_end () 228 }; 229 230 if (GNUNET_OK != 231 GNUNET_JSON_parse (json, 232 spec, 233 NULL, NULL)) 234 { 235 GNUNET_break_op (0); 236 pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 237 pr.hr.http_status = 0; 238 pr.hr.hint = "sig field missing in response"; 239 break; 240 } 241 242 if (GNUNET_OK != 243 parse_tokens (token_sigs, 244 &pr.details.ok.tokens, 245 &pr.details.ok.num_tokens)) 246 { 247 GNUNET_break_op (0); 248 pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 249 pr.hr.http_status = 0; 250 pr.hr.hint = "failed to parse token_sigs field in response"; 251 break; 252 } 253 254 if (GNUNET_OK != 255 TALER_merchant_pay_verify (&oph->h_contract_terms, 256 &oph->merchant_pub, 257 &pr.details.ok.merchant_sig)) 258 { 259 GNUNET_break_op (0); 260 pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 261 pr.hr.http_status = 0; 262 pr.hr.hint = "signature invalid"; 263 } 264 } 265 break; 266 /* Tolerating Not Acceptable because sometimes 267 * - especially in tests - we might want to POST 268 * coins one at a time. */ 269 case MHD_HTTP_NOT_ACCEPTABLE: 270 pr.hr.ec = TALER_JSON_get_error_code (json); 271 pr.hr.hint = TALER_JSON_get_error_hint (json); 272 break; 273 case MHD_HTTP_BAD_REQUEST: 274 pr.hr.ec = TALER_JSON_get_error_code (json); 275 pr.hr.hint = TALER_JSON_get_error_hint (json); 276 /* This should never happen, either us 277 * or the merchant is buggy (or API version conflict); 278 * just pass JSON reply to the application */ 279 break; 280 case MHD_HTTP_PAYMENT_REQUIRED: 281 /* was originally paid, but then refunded */ 282 pr.hr.ec = TALER_JSON_get_error_code (json); 283 pr.hr.hint = TALER_JSON_get_error_hint (json); 284 break; 285 case MHD_HTTP_FORBIDDEN: 286 pr.hr.ec = TALER_JSON_get_error_code (json); 287 pr.hr.hint = TALER_JSON_get_error_hint (json); 288 break; 289 case MHD_HTTP_NOT_FOUND: 290 pr.hr.ec = TALER_JSON_get_error_code (json); 291 pr.hr.hint = TALER_JSON_get_error_hint (json); 292 /* Nothing really to verify, this should never 293 happen, we should pass the JSON reply to the 294 application */ 295 break; 296 case MHD_HTTP_REQUEST_TIMEOUT: 297 pr.hr.ec = TALER_JSON_get_error_code (json); 298 pr.hr.hint = TALER_JSON_get_error_hint (json); 299 /* The merchant couldn't generate a timely response, likely because 300 it itself waited too long on the exchange. 301 Pass on to application. */ 302 break; 303 case MHD_HTTP_CONFLICT: 304 TALER_MERCHANT_parse_error_details_ (json, 305 MHD_HTTP_CONFLICT, 306 &pr.hr); 307 break; 308 case MHD_HTTP_GONE: 309 TALER_MERCHANT_parse_error_details_ (json, 310 response_code, 311 &pr.hr); 312 /* The merchant says we are too late, the offer has expired or some 313 denomination key of a coin involved has expired. 314 Might be a disagreement in timestamps? Still, pass on to application. */ 315 break; 316 case MHD_HTTP_PRECONDITION_FAILED: 317 TALER_MERCHANT_parse_error_details_ (json, 318 response_code, 319 &pr.hr); 320 /* Nothing really to verify, the merchant is blaming us for failing to 321 satisfy some constraint (likely it does not like our exchange because 322 of some disagreement on the PKI). We should pass the JSON reply to the 323 application */ 324 break; 325 case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: 326 { 327 json_t *ebus = json_object_get (json, 328 "exchange_base_urls"); 329 if (NULL == ebus) 330 { 331 GNUNET_break_op (0); 332 pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 333 pr.hr.http_status = 0; 334 pr.hr.hint = "failed to parse exchange_base_urls field in response"; 335 break; 336 } 337 { 338 size_t alen = json_array_size (ebus); 339 const char *ebua[GNUNET_NZL (alen)]; 340 size_t idx; 341 json_t *jebu; 342 bool ok = true; 343 344 GNUNET_assert (alen <= UINT_MAX); 345 json_array_foreach (ebus, idx, jebu) 346 { 347 ebua[idx] = json_string_value (jebu); 348 if (NULL == ebua[idx]) 349 { 350 GNUNET_break_op (0); 351 pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 352 pr.hr.http_status = 0; 353 pr.hr.hint = "non-string value in exchange_base_urls in response"; 354 ok = false; 355 break; 356 } 357 } 358 if (! ok) 359 break; 360 pr.details.unavailable_for_legal_reasons.num_exchanges 361 = (unsigned int) alen; 362 pr.details.unavailable_for_legal_reasons.exchanges 363 = ebua; 364 oph->pay_cb (oph->pay_cb_cls, 365 &pr); 366 TALER_MERCHANT_order_pay_cancel (oph); 367 return; 368 } 369 } 370 break; 371 case MHD_HTTP_INTERNAL_SERVER_ERROR: 372 TALER_MERCHANT_parse_error_details_ (json, 373 response_code, 374 &pr.hr); 375 /* Server had an internal issue; we should retry, 376 but this API leaves this to the application */ 377 break; 378 case MHD_HTTP_BAD_GATEWAY: 379 /* Nothing really to verify, the merchant is blaming the exchange. 380 We should pass the JSON reply to the application */ 381 TALER_MERCHANT_parse_error_details_ (json, 382 response_code, 383 &pr.hr); 384 break; 385 case MHD_HTTP_SERVICE_UNAVAILABLE: 386 TALER_MERCHANT_parse_error_details_ (json, 387 response_code, 388 &pr.hr); 389 /* Exchange couldn't respond properly; the retry is 390 left to the application */ 391 break; 392 case MHD_HTTP_GATEWAY_TIMEOUT: 393 TALER_MERCHANT_parse_error_details_ (json, 394 response_code, 395 &pr.hr); 396 /* Exchange couldn't respond in a timely fashion; 397 the retry is left to the application */ 398 break; 399 default: 400 TALER_MERCHANT_parse_error_details_ (json, 401 response_code, 402 &pr.hr); 403 /* unexpected response code */ 404 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 405 "Unexpected response code %u/%d\n", 406 (unsigned int) response_code, 407 (int) pr.hr.ec); 408 GNUNET_break_op (0); 409 break; 410 } 411 oph->pay_cb (oph->pay_cb_cls, 412 &pr); 413 TALER_MERCHANT_order_pay_cancel (oph); 414 } 415 416 417 struct TALER_MERCHANT_OrderPayHandle * 418 TALER_MERCHANT_order_pay_frontend ( 419 struct GNUNET_CURL_Context *ctx, 420 const char *merchant_url, 421 const char *order_id, 422 const char *session_id, 423 const json_t *wallet_data, 424 unsigned int num_coins, 425 const struct TALER_MERCHANT_PaidCoin coins[static num_coins], 426 unsigned int num_tokens, 427 const struct TALER_MERCHANT_UsedToken tokens[static num_tokens], 428 json_t *j_output_tokens, // FIXME: not used, remove? 429 TALER_MERCHANT_OrderPayCallback pay_cb, 430 void *pay_cb_cls) 431 { 432 struct TALER_MERCHANT_OrderPayHandle *oph; 433 json_t *pay_obj; 434 json_t *j_coins; 435 json_t *j_tokens = NULL; 436 CURL *eh; 437 struct TALER_Amount total_fee; 438 struct TALER_Amount total_amount; 439 440 j_coins = json_array (); 441 GNUNET_assert (NULL != j_coins); 442 for (unsigned int i = 0; i<num_coins; i++) 443 { 444 json_t *j_coin; 445 const struct TALER_MERCHANT_PaidCoin *pc = &coins[i]; 446 struct TALER_Amount fee; 447 struct TALER_DenominationHashP denom_hash; 448 449 if (0 > 450 TALER_amount_subtract (&fee, 451 &pc->amount_with_fee, 452 &pc->amount_without_fee)) 453 { 454 /* Integer underflow, fee larger than total amount? 455 This should not happen (client violated API!) */ 456 GNUNET_break (0); 457 json_decref (j_coins); 458 return NULL; 459 } 460 if (0 == i) 461 { 462 total_fee = fee; 463 total_amount = pc->amount_with_fee; 464 } 465 else 466 { 467 if ( (0 > 468 TALER_amount_add (&total_fee, 469 &total_fee, 470 &fee)) || 471 (0 > 472 TALER_amount_add (&total_amount, 473 &total_amount, 474 &pc->amount_with_fee)) ) 475 { 476 /* integer overflow */ 477 GNUNET_break (0); 478 json_decref (j_coins); 479 return NULL; 480 } 481 } 482 483 TALER_denom_pub_hash (&pc->denom_pub, 484 &denom_hash); 485 /* create JSON for this coin */ 486 j_coin = GNUNET_JSON_PACK ( 487 TALER_JSON_pack_amount ("contribution", 488 &pc->amount_with_fee), 489 GNUNET_JSON_pack_data_auto ("coin_pub", 490 &pc->coin_pub), 491 GNUNET_JSON_pack_string ("exchange_url", 492 pc->exchange_url), 493 GNUNET_JSON_pack_data_auto ("h_denom", 494 &denom_hash), 495 TALER_JSON_pack_denom_sig ("ub_sig", 496 &pc->denom_sig), 497 GNUNET_JSON_pack_data_auto ("coin_sig", 498 &pc->coin_sig)); 499 if (0 != 500 json_array_append_new (j_coins, 501 j_coin)) 502 { 503 GNUNET_break (0); 504 json_decref (j_coins); 505 return NULL; 506 } 507 } 508 509 if (0 < num_tokens) 510 { 511 j_tokens = json_array (); 512 GNUNET_assert (NULL != j_tokens); 513 for (unsigned int i = 0; i<num_tokens; i++) 514 { 515 json_t *j_token; 516 const struct TALER_MERCHANT_UsedToken *ut = &tokens[i]; 517 518 j_token = GNUNET_JSON_PACK ( 519 GNUNET_JSON_pack_data_auto ("token_sig", 520 &ut->token_sig), 521 GNUNET_JSON_pack_data_auto ("token_pub", 522 &ut->token_pub), 523 GNUNET_JSON_pack_data_auto ("h_issue", 524 &ut->issue_pub.public_key->pub_key_hash), 525 TALER_JSON_pack_token_issue_sig ("ub_sig", 526 &ut->ub_sig)); 527 if (0 != 528 json_array_append_new (j_tokens, 529 j_token)) 530 { 531 GNUNET_break (0); 532 json_decref (j_tokens); 533 return NULL; 534 } 535 } 536 } 537 538 pay_obj = GNUNET_JSON_PACK ( 539 GNUNET_JSON_pack_array_steal ("coins", 540 j_coins), 541 GNUNET_JSON_pack_allow_null ( 542 GNUNET_JSON_pack_array_steal ("tokens", 543 j_tokens)), 544 GNUNET_JSON_pack_allow_null ( 545 GNUNET_JSON_pack_object_incref ("wallet_data", 546 (json_t *) wallet_data)), 547 GNUNET_JSON_pack_allow_null ( 548 GNUNET_JSON_pack_string ("session_id", 549 session_id))); 550 551 json_dumpf (pay_obj, 552 stderr, 553 JSON_INDENT (2)); 554 555 oph = GNUNET_new (struct TALER_MERCHANT_OrderPayHandle); 556 oph->ctx = ctx; 557 oph->pay_cb = pay_cb; 558 oph->pay_cb_cls = pay_cb_cls; 559 { 560 char *path; 561 562 GNUNET_asprintf (&path, 563 "orders/%s/pay", 564 order_id); 565 oph->url = TALER_url_join (merchant_url, 566 path, 567 NULL); 568 GNUNET_free (path); 569 } 570 if (NULL == oph->url) 571 { 572 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 573 "Could not construct request URL.\n"); 574 json_decref (pay_obj); 575 GNUNET_free (oph); 576 return NULL; 577 } 578 oph->num_coins = num_coins; 579 oph->coins = GNUNET_new_array (num_coins, 580 struct TALER_MERCHANT_PaidCoin); 581 GNUNET_memcpy (oph->coins, 582 coins, 583 num_coins * sizeof (struct TALER_MERCHANT_PaidCoin)); 584 585 eh = TALER_MERCHANT_curl_easy_get_ (oph->url); 586 if (GNUNET_OK != 587 TALER_curl_easy_post (&oph->post_ctx, 588 eh, 589 pay_obj)) 590 { 591 GNUNET_break (0); 592 curl_easy_cleanup (eh); 593 json_decref (pay_obj); 594 GNUNET_free (oph->url); 595 GNUNET_free (oph); 596 return NULL; 597 } 598 json_decref (pay_obj); 599 oph->job = GNUNET_CURL_job_add2 (ctx, 600 eh, 601 oph->post_ctx.headers, 602 &handle_pay_finished, 603 oph); 604 return oph; 605 } 606 607 608 struct TALER_MERCHANT_OrderPayHandle * 609 TALER_MERCHANT_order_pay ( 610 struct GNUNET_CURL_Context *ctx, 611 const char *merchant_url, 612 const char *session_id, 613 const struct TALER_PrivateContractHashP *h_contract_terms, 614 int choice_index, 615 const struct TALER_Amount *amount, 616 const struct TALER_Amount *max_fee, 617 const struct TALER_MerchantPublicKeyP *merchant_pub, 618 const struct TALER_MerchantSignatureP *merchant_sig, 619 struct GNUNET_TIME_Timestamp timestamp, 620 struct GNUNET_TIME_Timestamp refund_deadline, 621 struct GNUNET_TIME_Timestamp pay_deadline, 622 const struct TALER_MerchantWireHashP *h_wire, 623 const char *order_id, 624 unsigned int num_coins, 625 const struct TALER_MERCHANT_PayCoin coins[static num_coins], 626 unsigned int num_tokens, 627 const struct TALER_MERCHANT_UseToken tokens[static num_tokens], 628 unsigned int num_output_tokens, 629 const struct TALER_MERCHANT_OutputToken output_tokens[static num_output_tokens 630 ], 631 TALER_MERCHANT_OrderPayCallback pay_cb, 632 void *pay_cb_cls) 633 { 634 json_t *j_output_tokens = NULL; 635 const json_t *wallet_data = NULL; 636 struct GNUNET_HashCode wallet_data_hash; 637 638 if (GNUNET_YES != 639 TALER_amount_cmp_currency (amount, 640 max_fee)) 641 { 642 GNUNET_break (0); 643 return NULL; 644 } 645 if ((0 < num_tokens || 0 < num_output_tokens) && 0 > choice_index) 646 { 647 /* Tokens (input or output) require a valid choice_index to be set. 648 Only contracts with coices can use or issue tokens. */ 649 GNUNET_break (0); 650 return NULL; 651 } 652 if (0 < num_output_tokens) 653 { 654 /* Build token envelopes json array. */ 655 j_output_tokens = json_array (); 656 GNUNET_assert (NULL != j_output_tokens); 657 for (unsigned int i = 0; i<num_output_tokens; i++) 658 { 659 json_t *j_token_ev; 660 const struct TALER_MERCHANT_OutputToken *ev = &output_tokens[i]; 661 662 j_token_ev = GNUNET_JSON_PACK ( 663 TALER_JSON_pack_token_envelope (NULL, 664 &ev->envelope)); 665 666 if (0 != 667 json_array_append_new (j_output_tokens, 668 j_token_ev)) 669 { 670 GNUNET_break (0); 671 json_decref (j_output_tokens); 672 return NULL; 673 } 674 } 675 } 676 if (0 <= choice_index) 677 { 678 wallet_data = GNUNET_JSON_PACK ( 679 GNUNET_JSON_pack_int64 ("choice_index", 680 choice_index), 681 GNUNET_JSON_pack_allow_null ( 682 GNUNET_JSON_pack_array_incref ("tokens_evs", 683 j_output_tokens))); 684 TALER_json_hash (wallet_data, 685 &wallet_data_hash); 686 } 687 { 688 struct TALER_MERCHANT_PaidCoin pc[num_coins]; 689 struct TALER_MERCHANT_UsedToken ut[num_tokens]; 690 691 for (unsigned int i = 0; i<num_coins; i++) 692 { 693 const struct TALER_MERCHANT_PayCoin *coin = &coins[i]; // coin priv. 694 struct TALER_MERCHANT_PaidCoin *p = &pc[i]; // coin pub. 695 struct TALER_Amount fee; 696 struct TALER_DenominationHashP h_denom_pub; 697 698 if (0 > 699 TALER_amount_subtract (&fee, 700 &coin->amount_with_fee, 701 &coin->amount_without_fee)) 702 { 703 /* Integer underflow, fee larger than total amount? 704 This should not happen (client violated API!) */ 705 GNUNET_break (0); 706 return NULL; 707 } 708 TALER_denom_pub_hash (&coin->denom_pub, 709 &h_denom_pub); 710 TALER_wallet_deposit_sign (&coin->amount_with_fee, 711 &fee, 712 h_wire, 713 h_contract_terms, 714 (NULL != wallet_data) 715 ? &wallet_data_hash 716 : NULL, 717 coin->h_age_commitment, 718 NULL /* h_extensions! */, 719 &h_denom_pub, 720 timestamp, 721 merchant_pub, 722 refund_deadline, 723 &coin->coin_priv, 724 &p->coin_sig); 725 p->denom_pub = coin->denom_pub; 726 p->denom_sig = coin->denom_sig; 727 p->denom_value = coin->denom_value; 728 GNUNET_CRYPTO_eddsa_key_get_public (&coin->coin_priv.eddsa_priv, 729 &p->coin_pub.eddsa_pub); 730 p->amount_with_fee = coin->amount_with_fee; 731 p->amount_without_fee = coin->amount_without_fee; 732 p->exchange_url = coin->exchange_url; 733 } 734 for (unsigned int i = 0; i<num_tokens; i++) 735 { 736 const struct TALER_MERCHANT_UseToken *token = &tokens[i]; 737 struct TALER_MERCHANT_UsedToken *t = &ut[i]; 738 739 TALER_wallet_token_use_sign (h_contract_terms, 740 &wallet_data_hash, // checked for != NULL above 741 &token->token_priv, 742 &t->token_sig); 743 t->ub_sig = token->ub_sig; 744 t->issue_pub = token->issue_pub; 745 GNUNET_CRYPTO_eddsa_key_get_public (&token->token_priv.private_key, 746 &t->token_pub.public_key); 747 } 748 { 749 struct TALER_MERCHANT_OrderPayHandle *oph; 750 751 oph = TALER_MERCHANT_order_pay_frontend (ctx, 752 merchant_url, 753 order_id, 754 session_id, 755 wallet_data, 756 num_coins, 757 pc, 758 num_tokens, 759 ut, 760 j_output_tokens, 761 pay_cb, 762 pay_cb_cls); 763 if (NULL == oph) 764 return NULL; 765 oph->h_contract_terms = *h_contract_terms; 766 oph->merchant_pub = *merchant_pub; 767 oph->am_wallet = true; 768 return oph; 769 } 770 } 771 } 772 773 774 void 775 TALER_MERCHANT_order_pay_cancel (struct TALER_MERCHANT_OrderPayHandle *oph) 776 { 777 if (NULL != oph->job) 778 { 779 GNUNET_CURL_job_cancel (oph->job); 780 oph->job = NULL; 781 } 782 TALER_curl_easy_post_finished (&oph->post_ctx); 783 json_decref (oph->error_history); 784 json_decref (oph->full_reply); 785 GNUNET_free (oph->coins); 786 GNUNET_free (oph->url); 787 GNUNET_free (oph); 788 } 789 790 791 /* end of merchant_api_post_order_pay.c */