Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2021 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, but
11 : 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 Public
16 : License along with TALER; see the file COPYING.LGPL. If not,
17 : see <http://www.gnu.org/licenses/>
18 : */
19 : /**
20 : * @file merchant_api_post_orders.c
21 : * @brief Implementation of the POST /orders
22 : * @author Christian Grothoff
23 : * @author Marcello Stanisci
24 : */
25 : #include "platform.h"
26 : #include <curl/curl.h>
27 : #include <jansson.h>
28 : #include <microhttpd.h> /* just for HTTP status codes */
29 : #include <gnunet/gnunet_util_lib.h>
30 : #include <gnunet/gnunet_curl_lib.h>
31 : #include "taler_merchant_service.h"
32 : #include "merchant_api_curl_defaults.h"
33 : #include <taler/taler_json_lib.h>
34 : #include <taler/taler_signatures.h>
35 : #include <taler/taler_curl_lib.h>
36 :
37 :
38 : /**
39 : * @brief A POST /orders Handle
40 : */
41 : struct TALER_MERCHANT_PostOrdersHandle
42 : {
43 :
44 : /**
45 : * The url for this request.
46 : */
47 : char *url;
48 :
49 : /**
50 : * Handle for the request.
51 : */
52 : struct GNUNET_CURL_Job *job;
53 :
54 : /**
55 : * Function to call with the result.
56 : */
57 : TALER_MERCHANT_PostOrdersCallback cb;
58 :
59 : /**
60 : * Closure for @a cb.
61 : */
62 : void *cb_cls;
63 :
64 : /**
65 : * Reference to the execution context.
66 : */
67 : struct GNUNET_CURL_Context *ctx;
68 :
69 : /**
70 : * Minor context that holds body and headers.
71 : */
72 : struct TALER_CURL_PostContext post_ctx;
73 : };
74 :
75 :
76 : /**
77 : * Function called when we're done processing the
78 : * HTTP POST /orders request.
79 : *
80 : * @param cls the `struct TALER_MERCHANT_PostOrdersHandle`
81 : * @param response_code HTTP response code, 0 on error
82 : * @param response response body, NULL if not JSON
83 : */
84 : static void
85 0 : handle_post_order_finished (void *cls,
86 : long response_code,
87 : const void *response)
88 : {
89 0 : struct TALER_MERCHANT_PostOrdersHandle *po = cls;
90 0 : const json_t *json = response;
91 0 : struct TALER_MERCHANT_PostOrdersReply por = {
92 0 : .hr.http_status = (unsigned int) response_code,
93 : .hr.reply = json
94 : };
95 : struct TALER_ClaimTokenP token;
96 :
97 0 : po->job = NULL;
98 0 : switch (response_code)
99 : {
100 0 : case 0:
101 0 : por.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
102 0 : break;
103 0 : case MHD_HTTP_OK:
104 : {
105 : bool no_token;
106 : struct GNUNET_JSON_Specification spec[] = {
107 0 : GNUNET_JSON_spec_string ("order_id",
108 : &por.details.ok.order_id),
109 0 : GNUNET_JSON_spec_mark_optional (
110 : GNUNET_JSON_spec_fixed_auto ("token",
111 : &token),
112 : &no_token),
113 0 : GNUNET_JSON_spec_end ()
114 : };
115 :
116 0 : if (GNUNET_OK !=
117 0 : GNUNET_JSON_parse (json,
118 : spec,
119 : NULL, NULL))
120 : {
121 0 : GNUNET_break_op (0);
122 0 : por.hr.http_status = 0;
123 0 : por.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
124 : }
125 : else
126 : {
127 0 : if (! no_token)
128 0 : por.details.ok.token = &token;
129 : }
130 : }
131 0 : break;
132 0 : case MHD_HTTP_BAD_REQUEST:
133 0 : json_dumpf (json,
134 : stderr,
135 : JSON_INDENT (2));
136 0 : por.hr.ec = TALER_JSON_get_error_code (json);
137 0 : por.hr.hint = TALER_JSON_get_error_hint (json);
138 : /* This should never happen, either us or
139 : the merchant is buggy (or API version conflict);
140 : just pass JSON reply to the application */
141 0 : break;
142 0 : case MHD_HTTP_UNAUTHORIZED:
143 0 : por.hr.ec = TALER_JSON_get_error_code (json);
144 0 : por.hr.hint = TALER_JSON_get_error_hint (json);
145 : /* Nothing really to verify, merchant says we need to authenticate. */
146 0 : break;
147 0 : case MHD_HTTP_FORBIDDEN:
148 : /* Nothing really to verify, merchant says one
149 : of the signatures is invalid; as we checked them,
150 : this should never happen, we should pass the JSON
151 : reply to the application */
152 0 : por.hr.ec = TALER_JSON_get_error_code (json);
153 0 : por.hr.hint = TALER_JSON_get_error_hint (json);
154 0 : break;
155 0 : case MHD_HTTP_NOT_FOUND:
156 : /* Nothing really to verify, this should never
157 : happen, we should pass the JSON reply to the application */
158 0 : por.hr.ec = TALER_JSON_get_error_code (json);
159 0 : por.hr.hint = TALER_JSON_get_error_hint (json);
160 0 : break;
161 0 : case MHD_HTTP_CONFLICT:
162 0 : por.hr.ec = TALER_JSON_get_error_code (json);
163 0 : por.hr.hint = TALER_JSON_get_error_hint (json);
164 0 : break;
165 0 : case MHD_HTTP_GONE:
166 : /* The quantity of some product requested was not available. */
167 : {
168 :
169 : struct GNUNET_JSON_Specification spec[] = {
170 0 : GNUNET_JSON_spec_string (
171 : "product_id",
172 : &por.details.gone.product_id),
173 0 : GNUNET_JSON_spec_uint64 (
174 : "requested_quantity",
175 : &por.details.gone.requested_quantity),
176 0 : GNUNET_JSON_spec_uint64 (
177 : "available_quantity",
178 : &por.details.gone.available_quantity),
179 0 : GNUNET_JSON_spec_mark_optional (
180 : GNUNET_JSON_spec_timestamp (
181 : "restock_expected",
182 : &por.details.gone.restock_expected),
183 : NULL),
184 0 : GNUNET_JSON_spec_end ()
185 : };
186 :
187 0 : if (GNUNET_OK !=
188 0 : GNUNET_JSON_parse (json,
189 : spec,
190 : NULL, NULL))
191 : {
192 0 : GNUNET_break_op (0);
193 0 : por.hr.http_status = 0;
194 0 : por.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
195 : }
196 0 : break;
197 : }
198 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
199 : /* Server had an internal issue; we should retry,
200 : but this API leaves this to the application */
201 0 : por.hr.ec = TALER_JSON_get_error_code (json);
202 0 : por.hr.hint = TALER_JSON_get_error_hint (json);
203 0 : break;
204 0 : default:
205 : /* unexpected response code */
206 0 : por.hr.ec = TALER_JSON_get_error_code (json);
207 0 : por.hr.hint = TALER_JSON_get_error_hint (json);
208 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
209 : "Unexpected response code %u/%d\n",
210 : (unsigned int) response_code,
211 : (int) por.hr.ec);
212 0 : GNUNET_break_op (0);
213 0 : break;
214 : }
215 0 : po->cb (po->cb_cls,
216 : &por);
217 0 : TALER_MERCHANT_orders_post_cancel (po);
218 0 : }
219 :
220 :
221 : struct TALER_MERCHANT_PostOrdersHandle *
222 0 : TALER_MERCHANT_orders_post (struct GNUNET_CURL_Context *ctx,
223 : const char *backend_url,
224 : const json_t *order,
225 : struct GNUNET_TIME_Relative refund_delay,
226 : TALER_MERCHANT_PostOrdersCallback cb,
227 : void *cb_cls)
228 : {
229 0 : return TALER_MERCHANT_orders_post2 (ctx,
230 : backend_url,
231 : order,
232 : refund_delay,
233 : NULL,
234 : 0,
235 : NULL,
236 : 0,
237 : NULL,
238 : true,
239 : cb,
240 : cb_cls);
241 : }
242 :
243 :
244 : struct TALER_MERCHANT_PostOrdersHandle *
245 0 : TALER_MERCHANT_orders_post2 (
246 : struct GNUNET_CURL_Context *ctx,
247 : const char *backend_url,
248 : const json_t *order,
249 : struct GNUNET_TIME_Relative refund_delay,
250 : const char *payment_target,
251 : unsigned int inventory_products_length,
252 : const struct TALER_MERCHANT_InventoryProduct inventory_products[],
253 : unsigned int uuids_length,
254 : const char *uuids[],
255 : bool create_token,
256 : TALER_MERCHANT_PostOrdersCallback cb,
257 : void *cb_cls)
258 : {
259 : struct TALER_MERCHANT_PostOrdersHandle *po;
260 : json_t *req;
261 : CURL *eh;
262 :
263 0 : po = GNUNET_new (struct TALER_MERCHANT_PostOrdersHandle);
264 0 : po->ctx = ctx;
265 0 : po->cb = cb;
266 0 : po->cb_cls = cb_cls;
267 0 : po->url = TALER_url_join (backend_url,
268 : "private/orders",
269 : NULL);
270 0 : req = GNUNET_JSON_PACK (
271 : GNUNET_JSON_pack_object_incref ("order",
272 : (json_t *) order),
273 : GNUNET_JSON_pack_allow_null (
274 : GNUNET_JSON_pack_string ("payment_target",
275 : payment_target)));
276 0 : if (0 != refund_delay.rel_value_us)
277 : {
278 0 : GNUNET_assert (0 ==
279 : json_object_set_new (req,
280 : "refund_delay",
281 : GNUNET_JSON_from_time_rel (
282 : refund_delay)));
283 : }
284 0 : if (0 != inventory_products_length)
285 : {
286 0 : json_t *ipa = json_array ();
287 :
288 0 : GNUNET_assert (NULL != ipa);
289 0 : for (unsigned int i = 0; i<inventory_products_length; i++)
290 : {
291 : json_t *ip;
292 :
293 0 : ip = GNUNET_JSON_PACK (
294 : GNUNET_JSON_pack_string ("product_id",
295 : inventory_products[i].product_id),
296 : GNUNET_JSON_pack_uint64 ("quantity",
297 : inventory_products[i].quantity));
298 0 : GNUNET_assert (NULL != ip);
299 0 : GNUNET_assert (0 ==
300 : json_array_append_new (ipa,
301 : ip));
302 : }
303 0 : GNUNET_assert (0 ==
304 : json_object_set_new (req,
305 : "inventory_products",
306 : ipa));
307 : }
308 0 : if (0 != uuids_length)
309 : {
310 0 : json_t *ua = json_array ();
311 :
312 0 : GNUNET_assert (NULL != ua);
313 0 : for (unsigned int i = 0; i<uuids_length; i++)
314 : {
315 : json_t *u;
316 :
317 0 : u = json_string (uuids[i]);
318 0 : GNUNET_assert (0 ==
319 : json_array_append_new (ua,
320 : u));
321 : }
322 0 : GNUNET_assert (0 ==
323 : json_object_set_new (req,
324 : "lock_uuids",
325 : ua));
326 : }
327 0 : if (! create_token)
328 : {
329 0 : GNUNET_assert (0 ==
330 : json_object_set_new (req,
331 : "create_token",
332 : json_boolean (create_token)));
333 : }
334 0 : eh = TALER_MERCHANT_curl_easy_get_ (po->url);
335 0 : if (GNUNET_OK !=
336 0 : TALER_curl_easy_post (&po->post_ctx,
337 : eh,
338 : req))
339 : {
340 0 : GNUNET_break (0);
341 0 : curl_easy_cleanup (eh);
342 0 : json_decref (req);
343 0 : GNUNET_free (po);
344 0 : return NULL;
345 : }
346 0 : json_decref (req);
347 0 : po->job = GNUNET_CURL_job_add2 (ctx,
348 : eh,
349 0 : po->post_ctx.headers,
350 : &handle_post_order_finished,
351 : po);
352 0 : return po;
353 : }
354 :
355 :
356 : void
357 0 : TALER_MERCHANT_orders_post_cancel (
358 : struct TALER_MERCHANT_PostOrdersHandle *po)
359 : {
360 0 : if (NULL != po->job)
361 : {
362 0 : GNUNET_CURL_job_cancel (po->job);
363 0 : po->job = NULL;
364 : }
365 0 : GNUNET_free (po->url);
366 0 : TALER_curl_easy_post_finished (&po->post_ctx);
367 0 : GNUNET_free (po);
368 0 : }
369 :
370 :
371 : /* end of merchant_api_post_orders.c */
|