Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2018, 2020 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU General Public License as
7 : published by the Free Software Foundation; either version 3, or
8 : (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, see
17 : <http://www.gnu.org/licenses/>
18 : */
19 : /**
20 : * @file testing_api_cmd_pay_order.c
21 : * @brief command to test the /orders/ID/pay feature.
22 : * @author Marcello Stanisci
23 : * @author Christian Grothoff
24 : */
25 : #include "platform.h"
26 : #include <taler/taler_exchange_service.h>
27 : #include <taler/taler_testing_lib.h>
28 : #include <taler/taler_signatures.h>
29 : #include "taler_merchant_service.h"
30 : #include "taler_merchant_testing_lib.h"
31 :
32 :
33 : /**
34 : * State for a /pay CMD.
35 : */
36 : struct PayState
37 : {
38 : /**
39 : * Contract terms hash code.
40 : */
41 : struct TALER_PrivateContractHashP h_contract_terms;
42 :
43 : /**
44 : * The interpreter state.
45 : */
46 : struct TALER_TESTING_Interpreter *is;
47 :
48 : /**
49 : * Expected HTTP response status code.
50 : */
51 : unsigned int http_status;
52 :
53 : /**
54 : * Reference to a command that can provide a order id,
55 : * typically a /proposal test command.
56 : */
57 : const char *proposal_reference;
58 :
59 : /**
60 : * Reference to a command that can provide a coin, so
61 : * we can pay here.
62 : */
63 : const char *coin_reference;
64 :
65 : /**
66 : * The merchant base URL.
67 : */
68 : const char *merchant_url;
69 :
70 : /**
71 : * Amount to be paid, plus the deposit fee.
72 : */
73 : const char *amount_with_fee;
74 :
75 : /**
76 : * Amount to be paid, including NO fees.
77 : */
78 : const char *amount_without_fee;
79 :
80 : /**
81 : * Handle to the pay operation.
82 : */
83 : struct TALER_MERCHANT_OrderPayHandle *oph;
84 :
85 : /**
86 : * Signature from the merchant, set on success.
87 : */
88 : struct TALER_MerchantSignatureP merchant_sig;
89 :
90 : /**
91 : * The session for which the payment is made.
92 : */
93 : const char *session_id;
94 : };
95 :
96 :
97 : /**
98 : * Parse the @a coins specification and grow the @a pc
99 : * array with the coins found, updating @a npc.
100 : *
101 : * @param[in,out] pc pointer to array of coins found
102 : * @param[in,out] npc length of array at @a pc
103 : * @param[in] coins string specifying coins to add to @a pc,
104 : * clobbered in the process
105 : * @param is interpreter state
106 : * @param amount_with_fee total amount to be paid for a contract.
107 : * @param amount_without_fee to be removed, there is no
108 : * per-contract fee, only per-coin exists.
109 : * @return #GNUNET_OK on success
110 : */
111 : static enum GNUNET_GenericReturnValue
112 0 : build_coins (struct TALER_MERCHANT_PayCoin **pc,
113 : unsigned int *npc,
114 : char *coins,
115 : struct TALER_TESTING_Interpreter *is,
116 : const char *amount_with_fee,
117 : const char *amount_without_fee)
118 : {
119 : char *token;
120 :
121 0 : for (token = strtok (coins, ";");
122 : NULL != token;
123 0 : token = strtok (NULL, ";"))
124 : {
125 : const struct TALER_TESTING_Command *coin_cmd;
126 : char *ctok;
127 : unsigned int ci;
128 : struct TALER_MERCHANT_PayCoin *icoin;
129 : const struct TALER_EXCHANGE_DenomPublicKey *dpk;
130 : const char **exchange_url;
131 :
132 : /* Token syntax is "LABEL[/NUMBER]" */
133 0 : ctok = strchr (token, '/');
134 0 : ci = 0;
135 0 : if (NULL != ctok)
136 : {
137 0 : *ctok = '\0';
138 0 : ctok++;
139 0 : if (1 != sscanf (ctok,
140 : "%u",
141 : &ci))
142 : {
143 0 : GNUNET_break (0);
144 0 : return GNUNET_SYSERR;
145 : }
146 : }
147 :
148 0 : coin_cmd = TALER_TESTING_interpreter_lookup_command
149 : (is, token);
150 :
151 0 : if (NULL == coin_cmd)
152 : {
153 0 : GNUNET_break (0);
154 0 : return GNUNET_SYSERR;
155 : }
156 :
157 0 : GNUNET_array_grow (*pc,
158 : *npc,
159 : (*npc) + 1);
160 :
161 0 : icoin = &((*pc)[(*npc) - 1]);
162 :
163 : {
164 : const struct TALER_CoinSpendPrivateKeyP *coin_priv;
165 : const struct TALER_DenominationSignature *denom_sig;
166 : const struct TALER_Amount *denom_value;
167 : const struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
168 : const struct TALER_AgeCommitmentHash *h_age_commitment;
169 :
170 0 : GNUNET_assert (GNUNET_OK ==
171 : TALER_TESTING_get_trait_coin_priv (coin_cmd,
172 : 0,
173 : &coin_priv));
174 0 : GNUNET_assert (GNUNET_OK ==
175 : TALER_TESTING_get_trait_denom_pub (coin_cmd,
176 : 0,
177 : &denom_pub));
178 0 : GNUNET_assert (GNUNET_OK ==
179 : TALER_TESTING_get_trait_denom_sig (coin_cmd,
180 : 0,
181 : &denom_sig));
182 0 : GNUNET_assert (GNUNET_OK ==
183 : TALER_TESTING_get_trait_amount (coin_cmd,
184 : &denom_value));
185 0 : GNUNET_assert (GNUNET_OK ==
186 : TALER_TESTING_get_trait_h_age_commitment (coin_cmd,
187 : 0,
188 : &h_age_commitment));
189 0 : icoin->coin_priv = *coin_priv;
190 0 : icoin->denom_pub = denom_pub->key;
191 0 : icoin->denom_sig = *denom_sig;
192 0 : icoin->denom_value = *denom_value;
193 0 : icoin->amount_with_fee = *denom_value;
194 0 : icoin->h_age_commitment = h_age_commitment;
195 : }
196 0 : GNUNET_assert (NULL != (dpk =
197 : TALER_TESTING_find_pk (is->keys,
198 : &icoin->denom_value,
199 : false)));
200 :
201 0 : GNUNET_assert (0 <=
202 : TALER_amount_subtract (&icoin->amount_without_fee,
203 : &icoin->denom_value,
204 : &dpk->fees.deposit));
205 0 : GNUNET_assert (GNUNET_OK ==
206 : TALER_TESTING_get_trait_exchange_url (coin_cmd,
207 : &exchange_url));
208 0 : icoin->exchange_url = *exchange_url;
209 : }
210 :
211 0 : return GNUNET_OK;
212 : }
213 :
214 :
215 : /**
216 : * Function called with the result of a /pay operation.
217 : * Checks whether the merchant signature is valid and the
218 : * HTTP response code matches our expectation.
219 : *
220 : * @param cls closure with the interpreter state
221 : * @param pr HTTP response
222 : */
223 : static void
224 0 : pay_cb (void *cls,
225 : const struct TALER_MERCHANT_PayResponse *pr)
226 : {
227 0 : struct PayState *ps = cls;
228 :
229 0 : ps->oph = NULL;
230 0 : if (ps->http_status != pr->hr.http_status)
231 : {
232 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
233 : "Unexpected response code %u (%d) to command %s\n",
234 : pr->hr.http_status,
235 : (int) pr->hr.ec,
236 : TALER_TESTING_interpreter_get_current_label (ps->is));
237 0 : TALER_TESTING_FAIL (ps->is);
238 : }
239 0 : if (MHD_HTTP_OK == pr->hr.http_status)
240 : {
241 0 : ps->merchant_sig = pr->details.success.merchant_sig;
242 : }
243 0 : TALER_TESTING_interpreter_next (ps->is);
244 : }
245 :
246 :
247 : /**
248 : * Run a "pay" CMD.
249 : *
250 : * @param cls closure.
251 : * @param cmd current CMD being run.
252 : * @param is interpreter state.
253 : */
254 : static void
255 0 : pay_run (void *cls,
256 : const struct TALER_TESTING_Command *cmd,
257 : struct TALER_TESTING_Interpreter *is)
258 : {
259 0 : struct PayState *ps = cls;
260 : const struct TALER_TESTING_Command *proposal_cmd;
261 : const json_t *contract_terms;
262 : const char *order_id;
263 : struct GNUNET_TIME_Timestamp refund_deadline;
264 : struct GNUNET_TIME_Timestamp pay_deadline;
265 : struct GNUNET_TIME_Timestamp timestamp;
266 : struct TALER_MerchantPublicKeyP merchant_pub;
267 : struct TALER_MerchantWireHashP h_wire;
268 : const struct TALER_PrivateContractHashP *h_proposal;
269 : struct TALER_Amount total_amount;
270 : struct TALER_Amount max_fee;
271 : const char *error_name;
272 : unsigned int error_line;
273 : struct TALER_MERCHANT_PayCoin *pay_coins;
274 : unsigned int npay_coins;
275 : const struct TALER_MerchantSignatureP *merchant_sig;
276 :
277 0 : ps->is = is;
278 0 : proposal_cmd = TALER_TESTING_interpreter_lookup_command (
279 : is,
280 : ps->proposal_reference);
281 :
282 0 : if (NULL == proposal_cmd)
283 0 : TALER_TESTING_FAIL (is);
284 :
285 0 : if (GNUNET_OK !=
286 0 : TALER_TESTING_get_trait_contract_terms (proposal_cmd,
287 : &contract_terms))
288 0 : TALER_TESTING_FAIL (is);
289 : {
290 : /* Get information that needs to be put verbatim in the
291 : * deposit permission */
292 : struct GNUNET_JSON_Specification spec[] = {
293 0 : GNUNET_JSON_spec_string ("order_id",
294 : &order_id),
295 0 : GNUNET_JSON_spec_timestamp ("refund_deadline",
296 : &refund_deadline),
297 0 : GNUNET_JSON_spec_timestamp ("pay_deadline",
298 : &pay_deadline),
299 0 : GNUNET_JSON_spec_timestamp ("timestamp",
300 : ×tamp),
301 0 : GNUNET_JSON_spec_fixed_auto ("merchant_pub",
302 : &merchant_pub),
303 0 : GNUNET_JSON_spec_fixed_auto ("h_wire",
304 : &h_wire),
305 0 : TALER_JSON_spec_amount_any ("amount",
306 : &total_amount),
307 0 : TALER_JSON_spec_amount_any ("max_fee",
308 : &max_fee),
309 : /* FIXME oec: parse minimum age, use data later? */
310 0 : GNUNET_JSON_spec_end ()
311 : };
312 :
313 0 : if (GNUNET_OK !=
314 0 : GNUNET_JSON_parse (contract_terms,
315 : spec,
316 : &error_name,
317 : &error_line))
318 : {
319 : char *js;
320 :
321 0 : js = json_dumps (contract_terms,
322 : JSON_INDENT (1));
323 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
324 : "Parser failed on %s:%u for input `%s'\n",
325 : error_name,
326 : error_line,
327 : js);
328 0 : free (js);
329 0 : TALER_TESTING_FAIL (is);
330 : }
331 : }
332 :
333 : {
334 : char *cr;
335 :
336 0 : cr = GNUNET_strdup (ps->coin_reference);
337 0 : pay_coins = NULL;
338 0 : npay_coins = 0;
339 0 : if (GNUNET_OK !=
340 0 : build_coins (&pay_coins,
341 : &npay_coins,
342 : cr,
343 : is,
344 : ps->amount_with_fee,
345 : ps->amount_without_fee))
346 : {
347 0 : GNUNET_array_grow (pay_coins,
348 : npay_coins,
349 : 0);
350 0 : GNUNET_free (cr);
351 0 : TALER_TESTING_FAIL (is);
352 : }
353 0 : GNUNET_free (cr);
354 : }
355 0 : if (GNUNET_OK !=
356 0 : TALER_TESTING_get_trait_merchant_sig (proposal_cmd,
357 : &merchant_sig))
358 0 : TALER_TESTING_FAIL (is);
359 :
360 0 : if (GNUNET_OK !=
361 0 : TALER_TESTING_get_trait_h_contract_terms (proposal_cmd,
362 : &h_proposal))
363 0 : TALER_TESTING_FAIL (is);
364 0 : ps->h_contract_terms = *h_proposal;
365 0 : ps->oph = TALER_MERCHANT_order_pay (is->ctx,
366 : ps->merchant_url,
367 : ps->session_id,
368 : h_proposal,
369 : &total_amount,
370 : &max_fee,
371 : &merchant_pub,
372 : merchant_sig,
373 : timestamp,
374 : refund_deadline,
375 : pay_deadline,
376 : &h_wire,
377 : order_id,
378 : npay_coins,
379 : pay_coins,
380 : &pay_cb,
381 : ps);
382 0 : GNUNET_array_grow (pay_coins,
383 : npay_coins,
384 : 0);
385 0 : if (NULL == ps->oph)
386 0 : TALER_TESTING_FAIL (is);
387 : }
388 :
389 :
390 : /**
391 : * Free a "pay" CMD, and cancel it if need be.
392 : *
393 : * @param cls closure.
394 : * @param cmd command currently being freed.
395 : */
396 : static void
397 0 : pay_cleanup (void *cls,
398 : const struct TALER_TESTING_Command *cmd)
399 : {
400 0 : struct PayState *ps = cls;
401 :
402 0 : if (NULL != ps->oph)
403 : {
404 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
405 : "Command `%s' did not complete.\n",
406 : TALER_TESTING_interpreter_get_current_label (
407 : ps->is));
408 0 : TALER_MERCHANT_order_pay_cancel (ps->oph);
409 : }
410 :
411 0 : GNUNET_free (ps);
412 0 : }
413 :
414 :
415 : /**
416 : * Offer internal data useful to other commands.
417 : *
418 : * @param cls closure
419 : * @param[out] ret result
420 : * @param trait name of the trait
421 : * @param index index number of the object to extract.
422 : * @return #GNUNET_OK on success
423 : */
424 : static enum GNUNET_GenericReturnValue
425 0 : pay_traits (void *cls,
426 : const void **ret,
427 : const char *trait,
428 : unsigned int index)
429 : {
430 :
431 0 : struct PayState *ps = cls;
432 : const char **order_id;
433 : const struct TALER_TESTING_Command *proposal_cmd;
434 : const struct TALER_MerchantPublicKeyP *merchant_pub;
435 :
436 0 : if (NULL ==
437 : (proposal_cmd =
438 0 : TALER_TESTING_interpreter_lookup_command (ps->is,
439 : ps->proposal_reference)))
440 : {
441 0 : GNUNET_break (0);
442 0 : return GNUNET_SYSERR;
443 : }
444 :
445 0 : if (GNUNET_OK !=
446 0 : TALER_TESTING_get_trait_order_id (proposal_cmd,
447 : &order_id))
448 : {
449 0 : GNUNET_break (0);
450 0 : return GNUNET_SYSERR;
451 : }
452 :
453 0 : if (GNUNET_OK !=
454 0 : TALER_TESTING_get_trait_merchant_pub (proposal_cmd,
455 : &merchant_pub))
456 : {
457 0 : GNUNET_break (0);
458 0 : return GNUNET_SYSERR;
459 : }
460 : {
461 : struct TALER_Amount amount_with_fee;
462 :
463 0 : GNUNET_assert (GNUNET_OK ==
464 : TALER_string_to_amount (ps->amount_with_fee,
465 : &amount_with_fee));
466 : {
467 : struct TALER_TESTING_Trait traits[] = {
468 0 : TALER_TESTING_make_trait_proposal_reference (&ps->proposal_reference),
469 0 : TALER_TESTING_make_trait_coin_reference (0,
470 : &ps->coin_reference),
471 0 : TALER_TESTING_make_trait_order_id (order_id),
472 0 : TALER_TESTING_make_trait_merchant_pub (merchant_pub),
473 0 : TALER_TESTING_make_trait_merchant_sig (&ps->merchant_sig),
474 0 : TALER_TESTING_make_trait_amount (&amount_with_fee),
475 0 : TALER_TESTING_trait_end ()
476 : };
477 :
478 0 : return TALER_TESTING_get_trait (traits,
479 : ret,
480 : trait,
481 : index);
482 : }
483 : }
484 : }
485 :
486 :
487 : struct TALER_TESTING_Command
488 0 : TALER_TESTING_cmd_merchant_pay_order (const char *label,
489 : const char *merchant_url,
490 : unsigned int http_status,
491 : const char *proposal_reference,
492 : const char *coin_reference,
493 : const char *amount_with_fee,
494 : const char *amount_without_fee,
495 : const char *session_id)
496 : {
497 : struct PayState *ps;
498 :
499 0 : ps = GNUNET_new (struct PayState);
500 0 : ps->http_status = http_status;
501 0 : ps->proposal_reference = proposal_reference;
502 0 : ps->coin_reference = coin_reference;
503 0 : ps->merchant_url = merchant_url;
504 0 : ps->amount_with_fee = amount_with_fee;
505 0 : ps->amount_without_fee = amount_without_fee;
506 0 : ps->session_id = session_id;
507 : {
508 0 : struct TALER_TESTING_Command cmd = {
509 : .cls = ps,
510 : .label = label,
511 : .run = &pay_run,
512 : .cleanup = &pay_cleanup,
513 : .traits = &pay_traits
514 : };
515 :
516 0 : return cmd;
517 : }
518 : }
519 :
520 :
521 : /* end of testing_api_cmd_pay_order.c */
|