Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2026 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Lesser General Public License as published by the Free Software
7 : Foundation; either version 2.1, 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 Lesser General Public License for more details.
12 :
13 : You should have received a copy of the GNU Lesser General Public License along with
14 : TALER; see the file COPYING.LGPL. If not, see
15 : <http://www.gnu.org/licenses/>
16 : */
17 : /**
18 : * @file src/lib/merchant_api_post-private-orders-ORDER_ID-collect.c
19 : * @brief Implementation of the POST /private/orders/$ORDER_ID/collect request
20 : * @author Bohdan Potuzhnyi
21 : * @author Volodymyr Potuzhnyi
22 : */
23 : #include "platform.h"
24 : #include <curl/curl.h>
25 : #include <jansson.h>
26 : #include <microhttpd.h> /* just for HTTP status codes */
27 : #include <gnunet/gnunet_util_lib.h>
28 : #include <gnunet/gnunet_curl_lib.h>
29 : #include <taler/merchant/post-private-orders-ORDER_ID-collect.h>
30 : #include "merchant_api_curl_defaults.h"
31 : #include "merchant_api_common.h"
32 : #include <taler/taler_json_lib.h>
33 : #include <taler/taler_curl_lib.h>
34 :
35 :
36 : /**
37 : * Handle for a POST /private/orders/$ORDER_ID/collect operation.
38 : */
39 : struct TALER_MERCHANT_PostPrivateOrdersCollectHandle
40 : {
41 : /**
42 : * Base URL of the merchant backend.
43 : */
44 : char *base_url;
45 :
46 : /**
47 : * The full URL for this request.
48 : */
49 : char *url;
50 :
51 : /**
52 : * Handle for the request.
53 : */
54 : struct GNUNET_CURL_Job *job;
55 :
56 : /**
57 : * Function to call with the result.
58 : */
59 : TALER_MERCHANT_PostPrivateOrdersCollectCallback cb;
60 :
61 : /**
62 : * Closure for @a cb.
63 : */
64 : TALER_MERCHANT_POST_PRIVATE_ORDERS_COLLECT_RESULT_CLOSURE *cb_cls;
65 :
66 : /**
67 : * Reference to the execution context.
68 : */
69 : struct GNUNET_CURL_Context *ctx;
70 :
71 : /**
72 : * Minor context that holds body and headers.
73 : */
74 : struct TALER_CURL_PostContext post_ctx;
75 :
76 : /**
77 : * Identifier of the order to collect.
78 : */
79 : char *order_id;
80 :
81 : /**
82 : * Session ID to store with the payment, NULL for none.
83 : */
84 : char *session_id;
85 :
86 : /**
87 : * Choice to complete, negative if the contract has no choices.
88 : */
89 : int16_t choice_index;
90 : };
91 :
92 :
93 : /**
94 : * Function called when we're done processing the
95 : * HTTP POST /private/orders/$ORDER_ID/collect request.
96 : *
97 : * @param cls the `struct TALER_MERCHANT_PostPrivateOrdersCollectHandle`
98 : * @param response_code HTTP response code, 0 on error
99 : * @param response response body, NULL if not in JSON
100 : */
101 : static void
102 20 : handle_collect_finished (void *cls,
103 : long response_code,
104 : const void *response)
105 : {
106 20 : struct TALER_MERCHANT_PostPrivateOrdersCollectHandle *poch = cls;
107 20 : const json_t *json = response;
108 20 : struct TALER_MERCHANT_PostPrivateOrdersCollectResponse cr = {
109 20 : .hr.http_status = (unsigned int) response_code,
110 : .hr.reply = json
111 : };
112 :
113 20 : poch->job = NULL;
114 20 : switch (response_code)
115 : {
116 0 : case 0:
117 0 : cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
118 0 : break;
119 6 : case MHD_HTTP_OK:
120 : {
121 : struct GNUNET_JSON_Specification spec[] = {
122 6 : GNUNET_JSON_spec_fixed_auto ("sig",
123 : &cr.details.ok.merchant_sig),
124 6 : GNUNET_JSON_spec_mark_optional (
125 : GNUNET_JSON_spec_string ("pos_confirmation",
126 : &cr.details.ok.pos_confirmation),
127 : NULL),
128 6 : GNUNET_JSON_spec_end ()
129 : };
130 :
131 : /* The response is the one of paying the order; "token_sigs" is
132 : never present, as orders with token outputs cannot be
133 : collected. */
134 6 : if (GNUNET_OK !=
135 6 : GNUNET_JSON_parse (json,
136 : spec,
137 : NULL, NULL))
138 : {
139 0 : GNUNET_break_op (0);
140 0 : cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
141 0 : cr.hr.http_status = 0;
142 0 : cr.hr.hint = "sig field missing in response";
143 : }
144 : }
145 6 : break;
146 0 : case MHD_HTTP_UNAUTHORIZED:
147 0 : cr.hr.ec = TALER_JSON_get_error_code (json);
148 0 : cr.hr.hint = TALER_JSON_get_error_hint (json);
149 0 : break;
150 2 : case MHD_HTTP_NOT_FOUND:
151 2 : cr.hr.ec = TALER_JSON_get_error_code (json);
152 2 : cr.hr.hint = TALER_JSON_get_error_hint (json);
153 2 : break;
154 6 : case MHD_HTTP_CONFLICT:
155 6 : cr.hr.ec = TALER_JSON_get_error_code (json);
156 6 : cr.hr.hint = TALER_JSON_get_error_hint (json);
157 6 : break;
158 6 : default:
159 6 : GNUNET_break_op (0);
160 6 : TALER_MERCHANT_parse_error_details_ (json,
161 : response_code,
162 : &cr.hr);
163 6 : break;
164 : }
165 20 : poch->cb (poch->cb_cls,
166 : &cr);
167 20 : TALER_MERCHANT_post_private_orders_collect_cancel (poch);
168 20 : }
169 :
170 :
171 : struct TALER_MERCHANT_PostPrivateOrdersCollectHandle *
172 20 : TALER_MERCHANT_post_private_orders_collect_create (
173 : struct GNUNET_CURL_Context *ctx,
174 : const char *url,
175 : const char *order_id,
176 : const char *session_id,
177 : int16_t choice_index)
178 : {
179 : struct TALER_MERCHANT_PostPrivateOrdersCollectHandle *poch;
180 :
181 20 : poch = GNUNET_new (struct TALER_MERCHANT_PostPrivateOrdersCollectHandle);
182 20 : poch->ctx = ctx;
183 20 : poch->base_url = GNUNET_strdup (url);
184 20 : poch->order_id = GNUNET_strdup (order_id);
185 20 : poch->choice_index = choice_index;
186 20 : if (NULL != session_id)
187 8 : poch->session_id = GNUNET_strdup (session_id);
188 20 : return poch;
189 : }
190 :
191 :
192 : enum TALER_ErrorCode
193 20 : TALER_MERCHANT_post_private_orders_collect_start (
194 : struct TALER_MERCHANT_PostPrivateOrdersCollectHandle *poch,
195 : TALER_MERCHANT_PostPrivateOrdersCollectCallback cb,
196 : TALER_MERCHANT_POST_PRIVATE_ORDERS_COLLECT_RESULT_CLOSURE *cb_cls)
197 : {
198 : json_t *req_obj;
199 : CURL *eh;
200 :
201 20 : poch->cb = cb;
202 20 : poch->cb_cls = cb_cls;
203 : {
204 : char *path;
205 :
206 20 : GNUNET_asprintf (&path,
207 : "private/orders/%s/collect",
208 : poch->order_id);
209 20 : poch->url = TALER_url_join (poch->base_url,
210 : path,
211 : NULL);
212 20 : GNUNET_free (path);
213 : }
214 20 : if (NULL == poch->url)
215 0 : return TALER_EC_GENERIC_CONFIGURATION_INVALID;
216 20 : req_obj = GNUNET_JSON_PACK (
217 : GNUNET_JSON_pack_allow_null (
218 : GNUNET_JSON_pack_string ("session_id",
219 : poch->session_id)));
220 20 : GNUNET_assert (NULL != req_obj);
221 20 : if (0 <= poch->choice_index)
222 8 : GNUNET_assert (0 ==
223 : json_object_set_new (req_obj,
224 : "choice_index",
225 : json_integer (poch->choice_index)));
226 20 : eh = TALER_MERCHANT_curl_easy_get_ (poch->url);
227 40 : if ( (NULL == eh) ||
228 : (GNUNET_OK !=
229 20 : TALER_curl_easy_post (&poch->post_ctx,
230 : eh,
231 : req_obj)) )
232 : {
233 0 : GNUNET_break (0);
234 0 : json_decref (req_obj);
235 0 : if (NULL != eh)
236 0 : curl_easy_cleanup (eh);
237 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
238 : }
239 20 : json_decref (req_obj);
240 40 : poch->job = GNUNET_CURL_job_add2 (poch->ctx,
241 : eh,
242 20 : poch->post_ctx.headers,
243 : &handle_collect_finished,
244 : poch);
245 20 : if (NULL == poch->job)
246 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
247 20 : return TALER_EC_NONE;
248 : }
249 :
250 :
251 : void
252 20 : TALER_MERCHANT_post_private_orders_collect_cancel (
253 : struct TALER_MERCHANT_PostPrivateOrdersCollectHandle *poch)
254 : {
255 20 : if (NULL != poch->job)
256 : {
257 0 : GNUNET_CURL_job_cancel (poch->job);
258 0 : poch->job = NULL;
259 : }
260 20 : TALER_curl_easy_post_finished (&poch->post_ctx);
261 20 : GNUNET_free (poch->order_id);
262 20 : GNUNET_free (poch->session_id);
263 20 : GNUNET_free (poch->url);
264 20 : GNUNET_free (poch->base_url);
265 20 : GNUNET_free (poch);
266 20 : }
267 :
268 :
269 : /* end of merchant_api_post-private-orders-ORDER_ID-collect.c */
|