Line data Source code
1 : /*
2 : This file is part of GNU Taler
3 : Copyright (C) 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 General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file util/contract_serialize.c
18 : * @brief shared logic for contract terms serialization
19 : * @author Iván Ávalos
20 : */
21 :
22 : #include "platform.h"
23 : #include <gnunet/gnunet_json_lib.h>
24 : #include <gnunet/gnunet_common.h>
25 : #include <taler/taler_json_lib.h>
26 : #include <jansson.h>
27 : #include "taler/taler_util.h"
28 : #include "taler_merchant_util.h"
29 :
30 : /**
31 : * Get JSON representation of merchant details.
32 : *
33 : * @param[in] contract contract terms
34 : * @return JSON object with merchant details; NULL on error
35 : */
36 : static json_t *
37 2 : json_from_merchant_details (
38 : const struct TALER_MERCHANT_Contract *contract)
39 : {
40 2 : return GNUNET_JSON_PACK (
41 : GNUNET_JSON_pack_string ("name",
42 : contract->merchant.name),
43 : GNUNET_JSON_pack_allow_null (
44 : GNUNET_JSON_pack_string ("email",
45 : contract->merchant.email)),
46 : GNUNET_JSON_pack_allow_null (
47 : GNUNET_JSON_pack_string ("website",
48 : contract->merchant.website)),
49 : GNUNET_JSON_pack_allow_null (
50 : GNUNET_JSON_pack_string ("logo",
51 : contract->merchant.logo)),
52 : GNUNET_JSON_pack_allow_null (
53 : GNUNET_JSON_pack_object_steal ("address",
54 : contract->merchant.address)),
55 : GNUNET_JSON_pack_allow_null (
56 : GNUNET_JSON_pack_object_steal ("jurisdiction",
57 : contract->merchant.jurisdiction)));
58 : }
59 :
60 :
61 : /**
62 : * Get JSON representation of contract choice input.
63 : *
64 : * @param[in] input contract terms choice input
65 : * @return JSON representation of @a input; NULL on error
66 : */
67 : static json_t *
68 6 : json_from_contract_input (
69 : const struct TALER_MERCHANT_ContractInput *input)
70 : {
71 6 : switch (input->type)
72 : {
73 0 : case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID:
74 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
75 : "invalid contract input type");
76 0 : GNUNET_assert (0);
77 : return NULL;
78 6 : case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN:
79 6 : return GNUNET_JSON_PACK (
80 : GNUNET_JSON_pack_string ("type",
81 : "token"),
82 : GNUNET_JSON_pack_string ("token_family_slug",
83 : input->details.token.token_family_slug),
84 : GNUNET_JSON_pack_int64 ("count",
85 : input->details.token.count));
86 : }
87 :
88 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
89 : "unsupported contract input type %d",
90 : input->type);
91 0 : GNUNET_assert (0);
92 : return NULL;
93 : }
94 :
95 :
96 : /**
97 : * Get JSON representation of contract choice output.
98 : *
99 : * @param[in] output contract terms choice output
100 : * @return JSON representation of @a output; NULL on error
101 : */
102 : static json_t *
103 10 : json_from_contract_output (
104 : const struct TALER_MERCHANT_ContractOutput *output)
105 : {
106 10 : switch (output->type)
107 : {
108 0 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
109 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
110 : "invalid contract output type");
111 0 : GNUNET_assert (0);
112 : return NULL;
113 9 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
114 9 : return GNUNET_JSON_PACK (
115 : GNUNET_JSON_pack_string ("type",
116 : "token"),
117 : GNUNET_JSON_pack_string ("token_family_slug",
118 : output->details.token.token_family_slug),
119 : GNUNET_JSON_pack_uint64 ("count",
120 : output->details.token.count),
121 : GNUNET_JSON_pack_uint64 ("key_index",
122 : output->details.token.key_index));
123 1 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
124 : {
125 : json_t *donau_urls;
126 :
127 1 : donau_urls = json_array ();
128 1 : GNUNET_assert (NULL != donau_urls);
129 :
130 1 : for (unsigned i = 0;
131 4 : i < output->details.donation_receipt.donau_urls_len;
132 3 : i++)
133 3 : GNUNET_assert (0 ==
134 : json_array_append (donau_urls,
135 : json_string (output->details.
136 : donation_receipt.
137 : donau_urls[i])));
138 :
139 1 : return GNUNET_JSON_PACK (
140 : GNUNET_JSON_pack_string ("type",
141 : "tax-receipt"),
142 : GNUNET_JSON_pack_array_steal ("donau_urls",
143 : donau_urls),
144 : GNUNET_JSON_pack_allow_null (
145 : TALER_JSON_pack_amount ("amount",
146 : &output->details.donation_receipt.amount)));
147 : }
148 : }
149 :
150 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
151 : "unsupported contract output type %d",
152 : output->type);
153 0 : GNUNET_assert (0);
154 : return NULL;
155 : }
156 :
157 :
158 : json_t *
159 11 : TALER_MERCHANT_json_from_contract_choice (
160 : const struct TALER_MERCHANT_ContractChoice *choice,
161 : bool order)
162 : {
163 : json_t *inputs;
164 : json_t *outputs;
165 :
166 11 : inputs = json_array ();
167 11 : GNUNET_assert (NULL != inputs);
168 17 : for (unsigned int i = 0; i < choice->inputs_len; i++)
169 6 : GNUNET_assert (0 ==
170 : json_array_append_new (inputs,
171 : json_from_contract_input (
172 : &choice->inputs[i])));
173 :
174 11 : outputs = json_array ();
175 11 : GNUNET_assert (NULL != outputs);
176 21 : for (unsigned int i = 0; i < choice->outputs_len; i++)
177 10 : GNUNET_assert (0 ==
178 : json_array_append_new (outputs,
179 : json_from_contract_output (
180 : &choice->outputs[i])));
181 :
182 11 : return GNUNET_JSON_PACK (
183 : TALER_JSON_pack_amount ("amount",
184 : &choice->amount),
185 : GNUNET_JSON_pack_allow_null (
186 : GNUNET_JSON_pack_string ("description",
187 : choice->description)),
188 : GNUNET_JSON_pack_allow_null (
189 : GNUNET_JSON_pack_object_steal ("description_i18n",
190 : choice->description_i18n)),
191 : (order)
192 : ? GNUNET_JSON_pack_allow_null (
193 : TALER_JSON_pack_amount ("max_fee",
194 : /* workaround for nullable amount */
195 : (GNUNET_OK ==
196 : TALER_amount_is_valid (&choice->max_fee))
197 : ? &choice->max_fee
198 : : NULL))
199 : : TALER_JSON_pack_amount ("max_fee",
200 : &choice->max_fee),
201 : (order)
202 : ? GNUNET_JSON_pack_allow_null (
203 : GNUNET_JSON_pack_array_steal ("inputs",
204 : inputs))
205 : : GNUNET_JSON_pack_array_steal ("inputs",
206 : inputs),
207 : (order)
208 : ? GNUNET_JSON_pack_allow_null (
209 : GNUNET_JSON_pack_array_steal ("outputs",
210 : outputs))
211 : : GNUNET_JSON_pack_array_steal ("outputs",
212 : outputs));
213 : }
214 :
215 :
216 : /**
217 : * Get JSON representation of contract token family key.
218 : *
219 : * @param[in] key contract token family key
220 : * @return JSON representation of @a key; NULL on error
221 : */
222 : static json_t *
223 10 : json_from_token_family_key (
224 : const struct TALER_MERCHANT_ContractTokenFamilyKey *key)
225 : {
226 10 : return GNUNET_JSON_PACK (
227 : GNUNET_JSON_pack_timestamp ("signature_validity_start",
228 : key->valid_after),
229 : GNUNET_JSON_pack_timestamp ("signature_validity_end",
230 : key->valid_before),
231 : TALER_JSON_pack_token_pub (NULL,
232 : &key->pub));
233 : }
234 :
235 :
236 : /**
237 : * Get JSON representation of contract token family details.
238 : *
239 : * @param[in] family contract token family
240 : * @return JSON representation of @a family->details; NULL on error
241 : */
242 : static json_t *
243 11 : json_from_token_family_details (
244 : const struct TALER_MERCHANT_ContractTokenFamily *family)
245 : {
246 11 : switch (family->kind)
247 : {
248 0 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
249 0 : break;
250 9 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
251 : {
252 : json_t *trusted_domains;
253 :
254 9 : trusted_domains = json_array ();
255 9 : GNUNET_assert (NULL != trusted_domains);
256 9 : for (unsigned int i = 0;
257 12 : i < family->details.subscription.trusted_domains_len;
258 3 : i++)
259 3 : GNUNET_assert (0 ==
260 : json_array_append_new (
261 : trusted_domains,
262 : json_string (
263 : family->details.subscription.trusted_domains[i])));
264 :
265 9 : return GNUNET_JSON_PACK (
266 : GNUNET_JSON_pack_string ("class",
267 : "subscription"),
268 : GNUNET_JSON_pack_array_steal ("trusted_domains",
269 : trusted_domains));
270 : }
271 2 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
272 : {
273 : json_t *expected_domains;
274 :
275 2 : expected_domains = json_array ();
276 2 : GNUNET_assert (NULL != expected_domains);
277 2 : for (unsigned int i = 0;
278 5 : i < family->details.discount.expected_domains_len;
279 3 : i++)
280 3 : GNUNET_assert (0 ==
281 : json_array_append_new (
282 : expected_domains,
283 : json_string (
284 : family->details.discount.expected_domains[i])));
285 :
286 2 : return GNUNET_JSON_PACK (
287 : GNUNET_JSON_pack_string ("class",
288 : "discount"),
289 : GNUNET_JSON_pack_array_steal ("expected_domains",
290 : expected_domains));
291 : }
292 : }
293 :
294 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
295 : "unsupported token family kind %d",
296 : family->kind);
297 0 : GNUNET_assert (0);
298 : return NULL;
299 : }
300 :
301 :
302 : json_t *
303 11 : TALER_MERCHANT_json_from_token_family (
304 : const struct TALER_MERCHANT_ContractTokenFamily *family)
305 : {
306 : json_t *keys;
307 :
308 11 : keys = json_array ();
309 11 : GNUNET_assert (NULL != keys);
310 21 : for (unsigned int i = 0; i < family->keys_len; i++)
311 10 : GNUNET_assert (0 == json_array_append_new (
312 : keys,
313 : json_from_token_family_key (
314 : &family->keys[i])));
315 :
316 11 : return GNUNET_JSON_PACK (
317 : GNUNET_JSON_pack_string ("name",
318 : family->name),
319 : GNUNET_JSON_pack_string ("description",
320 : family->description),
321 : GNUNET_JSON_pack_object_steal ("description_i18n",
322 : family->description_i18n),
323 : GNUNET_JSON_pack_array_steal ("keys",
324 : keys),
325 : GNUNET_JSON_pack_object_steal ("details",
326 : json_from_token_family_details (family)),
327 : GNUNET_JSON_pack_bool ("critical",
328 : family->critical));
329 : }
330 :
331 :
332 : /**
333 : * Get JSON object with contract terms v0-specific fields.
334 : *
335 : * @param[in] input contract terms v0
336 : * @return JSON object with @a input v0 fields; NULL on error
337 : */
338 : static json_t *
339 1 : json_from_contract_v0 (
340 : const struct TALER_MERCHANT_Contract *input)
341 : {
342 1 : return GNUNET_JSON_PACK (
343 : TALER_JSON_pack_amount ("amount",
344 : &input->details.v0.brutto),
345 : TALER_JSON_pack_amount ("max_fee",
346 : &input->details.v0.max_fee));
347 : }
348 :
349 :
350 : /**
351 : * Get JSON object with contract terms v1-specific fields.
352 : *
353 : * @param[in] input contract terms v1
354 : * @return JSON object with @a input v1 fields; NULL on error
355 : */
356 : static json_t *
357 1 : json_from_contract_v1 (
358 : const struct TALER_MERCHANT_Contract *input)
359 : {
360 : json_t *choices;
361 : json_t *families;
362 :
363 1 : choices = json_array ();
364 1 : GNUNET_assert (0 != choices);
365 2 : for (unsigned i = 0; i < input->details.v1.choices_len; i++)
366 1 : GNUNET_assert (0 == json_array_append_new (
367 : choices,
368 : TALER_MERCHANT_json_from_contract_choice (
369 : &input->details.v1.choices[i],
370 : false)));
371 :
372 1 : families = json_object ();
373 1 : GNUNET_assert (0 != families);
374 3 : for (unsigned i = 0; i < input->details.v1.token_authorities_len; i++)
375 2 : GNUNET_assert (0 == json_object_set_new (
376 : families,
377 : input->details.v1.token_authorities[i].slug,
378 : TALER_MERCHANT_json_from_token_family (
379 : &input->details.v1.token_authorities[i])));
380 :
381 1 : return GNUNET_JSON_PACK (
382 : GNUNET_JSON_pack_array_steal ("choices",
383 : choices),
384 : GNUNET_JSON_pack_object_steal ("token_families",
385 : families));
386 : }
387 :
388 :
389 : json_t *
390 2 : TALER_MERCHANT_contract_serialize (
391 : const struct TALER_MERCHANT_Contract *input,
392 : bool nonce_optional)
393 : {
394 : json_t *details;
395 :
396 2 : switch (input->version)
397 : {
398 1 : case TALER_MERCHANT_CONTRACT_VERSION_0:
399 1 : details = json_from_contract_v0 (input);
400 1 : goto success;
401 1 : case TALER_MERCHANT_CONTRACT_VERSION_1:
402 1 : details = json_from_contract_v1 (input);
403 1 : goto success;
404 : }
405 :
406 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
407 : "unknown contract type version %d",
408 : input->version);
409 0 : GNUNET_assert (0);
410 : return NULL;
411 :
412 2 : success:
413 2 : return GNUNET_JSON_PACK (
414 : GNUNET_JSON_pack_uint64 ("version",
415 : input->version),
416 : GNUNET_JSON_pack_string ("summary",
417 : input->summary),
418 : GNUNET_JSON_pack_allow_null (
419 : GNUNET_JSON_pack_object_steal ("summary_i18n",
420 : input->summary_i18n)),
421 : GNUNET_JSON_pack_string ("order_id",
422 : input->order_id),
423 : GNUNET_JSON_pack_allow_null (
424 : GNUNET_JSON_pack_string ("public_reorder_url",
425 : input->public_reorder_url)),
426 : GNUNET_JSON_pack_allow_null (
427 : GNUNET_JSON_pack_string ("fulfillment_url",
428 : input->fulfillment_url)),
429 : GNUNET_JSON_pack_allow_null (
430 : GNUNET_JSON_pack_string ("fulfillment_message",
431 : input->fulfillment_message)),
432 : GNUNET_JSON_pack_allow_null (
433 : GNUNET_JSON_pack_object_steal ("fulfillment_message_i18n",
434 : input->fulfillment_message_i18n)),
435 : GNUNET_JSON_pack_array_steal ("products",
436 : input->products),
437 : GNUNET_JSON_pack_timestamp ("timestamp",
438 : input->timestamp),
439 : GNUNET_JSON_pack_timestamp ("refund_deadline",
440 : input->refund_deadline),
441 : GNUNET_JSON_pack_timestamp ("pay_deadline",
442 : input->pay_deadline),
443 : GNUNET_JSON_pack_timestamp ("wire_transfer_deadline",
444 : input->wire_deadline),
445 : GNUNET_JSON_pack_data_auto ("merchant_pub",
446 : &input->merchant_pub.eddsa_pub),
447 : GNUNET_JSON_pack_string ("merchant_base_url",
448 : input->merchant_base_url),
449 : GNUNET_JSON_pack_object_steal ("merchant",
450 : json_from_merchant_details (input)),
451 : GNUNET_JSON_pack_data_auto ("h_wire",
452 : &input->h_wire),
453 : GNUNET_JSON_pack_string ("wire_method",
454 : input->wire_method),
455 : GNUNET_JSON_pack_array_steal ("exchanges",
456 : input->exchanges),
457 : GNUNET_JSON_pack_allow_null (
458 : GNUNET_JSON_pack_object_steal ("delivery_location",
459 : input->delivery_location)),
460 : GNUNET_JSON_pack_allow_null (
461 : GNUNET_JSON_pack_timestamp ("delivery_date",
462 : input->delivery_date)),
463 : (nonce_optional)
464 : ? GNUNET_JSON_pack_allow_null (
465 : GNUNET_JSON_pack_string ("nonce",
466 : input->nonce))
467 : : GNUNET_JSON_pack_string ("nonce",
468 : input->nonce),
469 : GNUNET_JSON_pack_allow_null (
470 : GNUNET_JSON_pack_time_rel ("auto_refund",
471 : input->auto_refund)),
472 : GNUNET_JSON_pack_allow_null (
473 : GNUNET_JSON_pack_object_steal ("extra",
474 : input->extra)),
475 : GNUNET_JSON_pack_allow_null (
476 : GNUNET_JSON_pack_uint64 ("minimum_age",
477 : input->minimum_age)),
478 : GNUNET_JSON_pack_object_steal (NULL,
479 : details));
480 : }
|