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 1 : for (unsigned i = 0;
130 4 : i < output->details.donation_receipt.donau_urls_len;
131 3 : i++)
132 3 : GNUNET_assert (0 ==
133 : json_array_append_new (
134 : donau_urls,
135 : json_string (
136 : output->details.donation_receipt.donau_urls[i])));
137 :
138 1 : return GNUNET_JSON_PACK (
139 : GNUNET_JSON_pack_string ("type",
140 : "tax-receipt"),
141 : GNUNET_JSON_pack_array_steal ("donau_urls",
142 : donau_urls),
143 : GNUNET_JSON_pack_allow_null (
144 : TALER_JSON_pack_amount ("amount",
145 : &output->details.donation_receipt.amount)));
146 : }
147 : }
148 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
149 : "Unsupported contract output type %d",
150 : output->type);
151 0 : GNUNET_assert (0);
152 : return NULL;
153 : }
154 :
155 :
156 : json_t *
157 11 : TALER_MERCHANT_json_from_contract_choice (
158 : const struct TALER_MERCHANT_ContractChoice *choice,
159 : bool order)
160 : {
161 : json_t *inputs;
162 : json_t *outputs;
163 :
164 11 : inputs = json_array ();
165 11 : GNUNET_assert (NULL != inputs);
166 17 : for (unsigned int i = 0; i < choice->inputs_len; i++)
167 6 : GNUNET_assert (0 ==
168 : json_array_append_new (inputs,
169 : json_from_contract_input (
170 : &choice->inputs[i])));
171 11 : outputs = json_array ();
172 11 : GNUNET_assert (NULL != outputs);
173 21 : for (unsigned int i = 0; i < choice->outputs_len; i++)
174 10 : GNUNET_assert (0 ==
175 : json_array_append_new (outputs,
176 : json_from_contract_output (
177 : &choice->outputs[i])));
178 :
179 11 : return GNUNET_JSON_PACK (
180 : TALER_JSON_pack_amount ("amount",
181 : &choice->amount),
182 : GNUNET_JSON_pack_allow_null (
183 : GNUNET_JSON_pack_string ("description",
184 : choice->description)),
185 : GNUNET_JSON_pack_allow_null (
186 : GNUNET_JSON_pack_object_incref ("description_i18n",
187 : choice->description_i18n)),
188 : (order)
189 : ? GNUNET_JSON_pack_allow_null (
190 : TALER_JSON_pack_amount (
191 : "max_fee",
192 : /* workaround for nullable amount */
193 : (GNUNET_OK ==
194 : TALER_amount_is_valid (&choice->max_fee))
195 : ? &choice->max_fee
196 : : NULL))
197 : : TALER_JSON_pack_amount ("max_fee",
198 : &choice->max_fee),
199 : (order)
200 : ? GNUNET_JSON_pack_allow_null (
201 : GNUNET_JSON_pack_array_steal ("inputs",
202 : inputs))
203 : : GNUNET_JSON_pack_array_steal ("inputs",
204 : inputs),
205 : (order)
206 : ? GNUNET_JSON_pack_allow_null (
207 : GNUNET_JSON_pack_array_steal ("outputs",
208 : outputs))
209 : : GNUNET_JSON_pack_array_steal ("outputs",
210 : outputs));
211 : }
212 :
213 :
214 : /**
215 : * Get JSON representation of contract token family key.
216 : *
217 : * @param[in] key contract token family key
218 : * @return JSON representation of @a key; NULL on error
219 : */
220 : static json_t *
221 10 : json_from_token_family_key (
222 : const struct TALER_MERCHANT_ContractTokenFamilyKey *key)
223 : {
224 10 : return GNUNET_JSON_PACK (
225 : GNUNET_JSON_pack_timestamp ("signature_validity_start",
226 : key->valid_after),
227 : GNUNET_JSON_pack_timestamp ("signature_validity_end",
228 : key->valid_before),
229 : TALER_JSON_pack_token_pub (NULL,
230 : &key->pub));
231 : }
232 :
233 :
234 : /**
235 : * Get JSON representation of contract token family details.
236 : *
237 : * @param[in] family contract token family
238 : * @return JSON representation of @a family->details; NULL on error
239 : */
240 : static json_t *
241 11 : json_from_token_family_details (
242 : const struct TALER_MERCHANT_ContractTokenFamily *family)
243 : {
244 11 : switch (family->kind)
245 : {
246 0 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
247 0 : break;
248 9 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
249 : {
250 : json_t *trusted_domains;
251 :
252 9 : trusted_domains = json_array ();
253 9 : GNUNET_assert (NULL != trusted_domains);
254 9 : for (unsigned int i = 0;
255 12 : i < family->details.subscription.trusted_domains_len;
256 3 : i++)
257 3 : GNUNET_assert (0 ==
258 : json_array_append_new (
259 : trusted_domains,
260 : json_string (
261 : family->details.subscription.trusted_domains[i])));
262 :
263 9 : return GNUNET_JSON_PACK (
264 : GNUNET_JSON_pack_string ("class",
265 : "subscription"),
266 : GNUNET_JSON_pack_array_steal ("trusted_domains",
267 : trusted_domains));
268 : }
269 2 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
270 : {
271 : json_t *expected_domains;
272 :
273 2 : expected_domains = json_array ();
274 2 : GNUNET_assert (NULL != expected_domains);
275 2 : for (unsigned int i = 0;
276 5 : i < family->details.discount.expected_domains_len;
277 3 : i++)
278 3 : GNUNET_assert (0 ==
279 : json_array_append_new (
280 : expected_domains,
281 : json_string (
282 : family->details.discount.expected_domains[i])));
283 :
284 2 : return GNUNET_JSON_PACK (
285 : GNUNET_JSON_pack_string ("class",
286 : "discount"),
287 : GNUNET_JSON_pack_array_steal ("expected_domains",
288 : expected_domains));
289 : }
290 : }
291 :
292 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
293 : "unsupported token family kind %d",
294 : family->kind);
295 0 : GNUNET_assert (0);
296 : return NULL;
297 : }
298 :
299 :
300 : json_t *
301 11 : TALER_MERCHANT_json_from_token_family (
302 : const struct TALER_MERCHANT_ContractTokenFamily *family)
303 : {
304 : json_t *keys;
305 :
306 11 : keys = json_array ();
307 11 : GNUNET_assert (NULL != keys);
308 21 : for (unsigned int i = 0; i < family->keys_len; i++)
309 10 : GNUNET_assert (0 == json_array_append_new (
310 : keys,
311 : json_from_token_family_key (
312 : &family->keys[i])));
313 :
314 11 : return GNUNET_JSON_PACK (
315 : GNUNET_JSON_pack_string ("name",
316 : family->name),
317 : GNUNET_JSON_pack_string ("description",
318 : family->description),
319 : GNUNET_JSON_pack_object_incref ("description_i18n",
320 : family->description_i18n),
321 : GNUNET_JSON_pack_array_steal ("keys",
322 : keys),
323 : GNUNET_JSON_pack_object_steal ("details",
324 : json_from_token_family_details (family)),
325 : GNUNET_JSON_pack_bool ("critical",
326 : family->critical));
327 : }
328 :
329 :
330 : /**
331 : * Get JSON object with contract terms v0-specific fields.
332 : *
333 : * @param[in] input contract terms v0
334 : * @return JSON object with @a input v0 fields; NULL on error
335 : */
336 : static json_t *
337 1 : json_from_contract_v0 (
338 : const struct TALER_MERCHANT_Contract *input)
339 : {
340 1 : return GNUNET_JSON_PACK (
341 : TALER_JSON_pack_amount ("amount",
342 : &input->details.v0.brutto),
343 : TALER_JSON_pack_amount ("max_fee",
344 : &input->details.v0.max_fee));
345 : }
346 :
347 :
348 : /**
349 : * Get JSON object with contract terms v1-specific fields.
350 : *
351 : * @param[in] input contract terms v1
352 : * @return JSON object with @a input v1 fields; NULL on error
353 : */
354 : static json_t *
355 1 : json_from_contract_v1 (
356 : const struct TALER_MERCHANT_Contract *input)
357 : {
358 : json_t *choices;
359 : json_t *families;
360 :
361 1 : choices = json_array ();
362 1 : GNUNET_assert (0 != choices);
363 2 : for (unsigned i = 0; i < input->details.v1.choices_len; i++)
364 1 : GNUNET_assert (0 == json_array_append_new (
365 : choices,
366 : TALER_MERCHANT_json_from_contract_choice (
367 : &input->details.v1.choices[i],
368 : false)));
369 :
370 1 : families = json_object ();
371 1 : GNUNET_assert (0 != families);
372 3 : for (unsigned i = 0; i < input->details.v1.token_authorities_len; i++)
373 2 : GNUNET_assert (0 == json_object_set_new (
374 : families,
375 : input->details.v1.token_authorities[i].slug,
376 : TALER_MERCHANT_json_from_token_family (
377 : &input->details.v1.token_authorities[i])));
378 :
379 1 : return GNUNET_JSON_PACK (
380 : GNUNET_JSON_pack_array_steal ("choices",
381 : choices),
382 : GNUNET_JSON_pack_object_steal ("token_families",
383 : families));
384 : }
385 :
386 :
387 : json_t *
388 2 : TALER_MERCHANT_contract_serialize (
389 : const struct TALER_MERCHANT_Contract *input,
390 : bool nonce_optional)
391 : {
392 : json_t *details;
393 :
394 2 : switch (input->version)
395 : {
396 1 : case TALER_MERCHANT_CONTRACT_VERSION_0:
397 1 : details = json_from_contract_v0 (input);
398 1 : goto success;
399 1 : case TALER_MERCHANT_CONTRACT_VERSION_1:
400 1 : details = json_from_contract_v1 (input);
401 1 : goto success;
402 : }
403 :
404 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
405 : "unknown contract type version %d",
406 : input->version);
407 0 : GNUNET_assert (0);
408 : return NULL;
409 :
410 2 : success:
411 2 : return GNUNET_JSON_PACK (
412 : GNUNET_JSON_pack_uint64 ("version",
413 : input->version),
414 : GNUNET_JSON_pack_string ("summary",
415 : input->summary),
416 : GNUNET_JSON_pack_allow_null (
417 : GNUNET_JSON_pack_object_steal ("summary_i18n",
418 : input->summary_i18n)),
419 : GNUNET_JSON_pack_string ("order_id",
420 : input->order_id),
421 : GNUNET_JSON_pack_allow_null (
422 : GNUNET_JSON_pack_string ("public_reorder_url",
423 : input->public_reorder_url)),
424 : GNUNET_JSON_pack_allow_null (
425 : GNUNET_JSON_pack_string ("fulfillment_url",
426 : input->fulfillment_url)),
427 : GNUNET_JSON_pack_allow_null (
428 : GNUNET_JSON_pack_string ("fulfillment_message",
429 : input->fulfillment_message)),
430 : GNUNET_JSON_pack_allow_null (
431 : GNUNET_JSON_pack_object_steal ("fulfillment_message_i18n",
432 : input->fulfillment_message_i18n)),
433 : GNUNET_JSON_pack_array_steal ("products",
434 : input->products),
435 : GNUNET_JSON_pack_timestamp ("timestamp",
436 : input->timestamp),
437 : GNUNET_JSON_pack_timestamp ("refund_deadline",
438 : input->refund_deadline),
439 : GNUNET_JSON_pack_timestamp ("pay_deadline",
440 : input->pay_deadline),
441 : GNUNET_JSON_pack_timestamp ("wire_transfer_deadline",
442 : input->wire_deadline),
443 : GNUNET_JSON_pack_data_auto ("merchant_pub",
444 : &input->merchant_pub.eddsa_pub),
445 : GNUNET_JSON_pack_string ("merchant_base_url",
446 : input->merchant_base_url),
447 : GNUNET_JSON_pack_object_steal ("merchant",
448 : json_from_merchant_details (input)),
449 : GNUNET_JSON_pack_data_auto ("h_wire",
450 : &input->h_wire),
451 : GNUNET_JSON_pack_string ("wire_method",
452 : input->wire_method),
453 : GNUNET_JSON_pack_array_steal ("exchanges",
454 : input->exchanges),
455 : GNUNET_JSON_pack_allow_null (
456 : GNUNET_JSON_pack_object_steal ("delivery_location",
457 : input->delivery_location)),
458 : GNUNET_JSON_pack_allow_null (
459 : GNUNET_JSON_pack_timestamp ("delivery_date",
460 : input->delivery_date)),
461 : (nonce_optional)
462 : ? GNUNET_JSON_pack_allow_null (
463 : GNUNET_JSON_pack_string ("nonce",
464 : input->nonce))
465 : : GNUNET_JSON_pack_string ("nonce",
466 : input->nonce),
467 : GNUNET_JSON_pack_allow_null (
468 : GNUNET_JSON_pack_time_rel ("auto_refund",
469 : input->auto_refund)),
470 : GNUNET_JSON_pack_allow_null (
471 : GNUNET_JSON_pack_object_steal ("extra",
472 : input->extra)),
473 : GNUNET_JSON_pack_allow_null (
474 : GNUNET_JSON_pack_uint64 ("minimum_age",
475 : input->minimum_age)),
476 : GNUNET_JSON_pack_object_steal (NULL,
477 : details));
478 : }
|