Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2018, 2020, 2022 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 merchant_api_wallet_get_order.c
19 : * @brief Implementation of the GET /orders/$ID request
20 : * @author Christian Grothoff
21 : * @author Marcello Stanisci
22 : * @author Florian Dold
23 : */
24 : #include "platform.h"
25 : #include <curl/curl.h>
26 : #include <jansson.h>
27 : #include <microhttpd.h> /* just for HTTP status codes */
28 : #include <gnunet/gnunet_util_lib.h>
29 : #include <gnunet/gnunet_curl_lib.h>
30 : #include "taler_merchant_service.h"
31 : #include "merchant_api_curl_defaults.h"
32 : #include <taler/taler_json_lib.h>
33 : #include <taler/taler_signatures.h>
34 :
35 :
36 : /**
37 : * @brief A GET /orders/$ID handle
38 : */
39 : struct TALER_MERCHANT_OrderWalletGetHandle
40 : {
41 :
42 : /**
43 : * The url for this request.
44 : */
45 : char *url;
46 :
47 : /**
48 : * Handle for the request.
49 : */
50 : struct GNUNET_CURL_Job *job;
51 :
52 : /**
53 : * Function to call with the result.
54 : */
55 : TALER_MERCHANT_OrderWalletGetCallback cb;
56 :
57 : /**
58 : * Closure for @a cb.
59 : */
60 : void *cb_cls;
61 :
62 : /**
63 : * Reference to the execution context.
64 : */
65 : struct GNUNET_CURL_Context *ctx;
66 : };
67 :
68 :
69 : /**
70 : * Convenience function to call the callback in @a owgh with an error code of
71 : * @a ec and the exchange body being set to @a reply.
72 : *
73 : * @param owgh handle providing callback
74 : * @param ec error code to return to application
75 : * @param reply JSON reply we got from the exchange, can be NULL
76 : */
77 : static void
78 0 : cb_failure (struct TALER_MERCHANT_OrderWalletGetHandle *owgh,
79 : enum TALER_ErrorCode ec,
80 : const json_t *reply)
81 : {
82 0 : struct TALER_MERCHANT_OrderWalletGetResponse owgr = {
83 : .hr.ec = ec,
84 : .hr.reply = reply
85 : };
86 :
87 0 : owgh->cb (owgh->cb_cls,
88 : &owgr);
89 0 : }
90 :
91 :
92 : /**
93 : * Function called when we're done processing the GET /check-payment request.
94 : *
95 : * @param cls the `struct TALER_MERCHANT_OrderWalletGetHandle`
96 : * @param response_code HTTP response code, 0 on error
97 : * @param response response body, should be NULL
98 : */
99 : static void
100 0 : handle_wallet_get_order_finished (void *cls,
101 : long response_code,
102 : const void *response)
103 : {
104 0 : struct TALER_MERCHANT_OrderWalletGetHandle *owgh = cls;
105 0 : const json_t *json = response;
106 :
107 0 : owgh->job = NULL;
108 0 : switch (response_code)
109 : {
110 0 : case MHD_HTTP_OK:
111 : {
112 0 : struct TALER_MERCHANT_OrderWalletGetResponse owgr = {
113 : .hr.reply = json,
114 : .hr.http_status = MHD_HTTP_OK
115 : };
116 : struct GNUNET_JSON_Specification spec[] = {
117 0 : GNUNET_JSON_spec_bool ("refunded",
118 : &owgr.details.success.refunded),
119 0 : GNUNET_JSON_spec_bool ("refund_pending",
120 : &owgr.details.success.refund_pending),
121 0 : TALER_JSON_spec_amount_any ("refund_amount",
122 : &owgr.details.success.refund_amount),
123 0 : GNUNET_JSON_spec_end ()
124 : };
125 :
126 0 : if (GNUNET_OK !=
127 0 : GNUNET_JSON_parse (json,
128 : spec,
129 : NULL, NULL))
130 : {
131 0 : GNUNET_break_op (0);
132 0 : cb_failure (owgh,
133 : TALER_EC_GENERIC_REPLY_MALFORMED,
134 : json);
135 0 : TALER_MERCHANT_wallet_order_get_cancel (owgh);
136 0 : return;
137 : }
138 0 : owgh->cb (owgh->cb_cls,
139 : &owgr);
140 0 : GNUNET_JSON_parse_free (spec);
141 0 : break;
142 : }
143 0 : case MHD_HTTP_PAYMENT_REQUIRED:
144 : {
145 0 : struct TALER_MERCHANT_OrderWalletGetResponse owgr = {
146 : .hr.reply = json,
147 : .hr.http_status = MHD_HTTP_PAYMENT_REQUIRED
148 : };
149 :
150 : /* Status is: unpaid */
151 : owgr.details.payment_required.taler_pay_uri
152 0 : = json_string_value (json_object_get (json,
153 : "taler_pay_uri"));
154 : owgr.details.payment_required.already_paid_order_id
155 0 : = json_string_value (json_object_get (json,
156 : "already_paid_order_id"));
157 0 : if (NULL == owgr.details.payment_required.taler_pay_uri)
158 : {
159 0 : GNUNET_break_op (0);
160 0 : cb_failure (owgh,
161 : TALER_EC_GENERIC_REPLY_MALFORMED,
162 : json);
163 0 : break;
164 : }
165 0 : owgh->cb (owgh->cb_cls,
166 : &owgr);
167 0 : break;
168 : }
169 0 : default:
170 : {
171 0 : struct TALER_MERCHANT_OrderWalletGetResponse owgr = {
172 : .hr.reply = json,
173 : .hr.http_status = response_code
174 : };
175 :
176 0 : TALER_MERCHANT_parse_error_details_ (response,
177 : response_code,
178 : &owgr.hr);
179 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
180 : "Checking order status failed with HTTP status code %u/%d\n",
181 : (unsigned int) response_code,
182 : (int) owgr.hr.ec);
183 0 : GNUNET_break_op (0);
184 0 : owgh->cb (owgh->cb_cls,
185 : &owgr);
186 0 : break;
187 : }
188 : }
189 0 : TALER_MERCHANT_wallet_order_get_cancel (owgh);
190 : }
191 :
192 :
193 : struct TALER_MERCHANT_OrderWalletGetHandle *
194 0 : TALER_MERCHANT_wallet_order_get (
195 : struct GNUNET_CURL_Context *ctx,
196 : const char *backend_url,
197 : const char *order_id,
198 : const struct TALER_PrivateContractHashP *h_contract,
199 : struct GNUNET_TIME_Relative timeout,
200 : const char *session_id,
201 : const struct TALER_Amount *min_refund,
202 : bool await_refund_obtained,
203 : TALER_MERCHANT_OrderWalletGetCallback cb,
204 : void *cb_cls)
205 : {
206 : struct TALER_MERCHANT_OrderWalletGetHandle *owgh;
207 : unsigned long long tms;
208 : long tlong;
209 :
210 0 : GNUNET_assert (NULL != backend_url);
211 0 : GNUNET_assert (NULL != order_id);
212 0 : owgh = GNUNET_new (struct TALER_MERCHANT_OrderWalletGetHandle);
213 0 : owgh->ctx = ctx;
214 0 : owgh->cb = cb;
215 0 : owgh->cb_cls = cb_cls;
216 0 : tms = (unsigned long long) (timeout.rel_value_us
217 0 : / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us);
218 : /* set curl timeout to *our* long poll timeout plus one minute
219 : (for network latency and processing delays) */
220 0 : tlong = (long) (GNUNET_TIME_relative_add (timeout,
221 0 : GNUNET_TIME_UNIT_MINUTES).
222 : rel_value_us
223 0 : / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us);
224 : {
225 : char timeout_ms[32];
226 : struct GNUNET_CRYPTO_HashAsciiEncoded h_contract_s;
227 : char *path;
228 :
229 0 : GNUNET_CRYPTO_hash_to_enc (&h_contract->hash,
230 : &h_contract_s);
231 0 : GNUNET_snprintf (timeout_ms,
232 : sizeof (timeout_ms),
233 : "%llu",
234 : tms);
235 0 : GNUNET_asprintf (&path,
236 : "orders/%s",
237 : order_id);
238 0 : owgh->url = TALER_url_join (backend_url,
239 : path,
240 : "h_contract",
241 : h_contract_s.encoding,
242 : "session_id",
243 : session_id,
244 : "timeout_ms",
245 : (0 != tms)
246 : ? timeout_ms
247 : : NULL,
248 : "refund",
249 : (NULL != min_refund)
250 0 : ? TALER_amount2s (min_refund)
251 : : NULL,
252 : "await_refund_obtained",
253 : await_refund_obtained
254 : ? "yes"
255 : : NULL,
256 : NULL);
257 0 : GNUNET_free (path);
258 : }
259 0 : if (NULL == owgh->url)
260 : {
261 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
262 : "Could not construct request URL.\n");
263 0 : GNUNET_free (owgh);
264 0 : return NULL;
265 : }
266 :
267 : {
268 : CURL *eh;
269 :
270 0 : eh = TALER_MERCHANT_curl_easy_get_ (owgh->url);
271 0 : if (0 != tms)
272 : {
273 0 : if (CURLE_OK !=
274 0 : curl_easy_setopt (eh,
275 : CURLOPT_TIMEOUT_MS,
276 : (long) tms))
277 : {
278 0 : GNUNET_break (0);
279 0 : curl_easy_cleanup (eh);
280 0 : GNUNET_free (owgh->url);
281 0 : GNUNET_free (owgh);
282 0 : return NULL;
283 : }
284 : }
285 0 : if (CURLE_OK !=
286 0 : curl_easy_setopt (eh,
287 : CURLOPT_TIMEOUT_MS,
288 : tlong))
289 : {
290 0 : GNUNET_break (0);
291 0 : curl_easy_cleanup (eh);
292 0 : GNUNET_free (owgh->url);
293 0 : GNUNET_free (owgh);
294 0 : return NULL;
295 : }
296 :
297 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
298 : "Checking order status at %s\n",
299 : owgh->url);
300 0 : if (NULL == (owgh->job =
301 0 : GNUNET_CURL_job_add (ctx,
302 : eh,
303 : &handle_wallet_get_order_finished,
304 : owgh)))
305 : {
306 0 : GNUNET_break (0);
307 0 : GNUNET_free (owgh->url);
308 0 : GNUNET_free (owgh);
309 0 : return NULL;
310 : }
311 : }
312 0 : return owgh;
313 : }
314 :
315 :
316 : void
317 0 : TALER_MERCHANT_wallet_order_get_cancel (
318 : struct TALER_MERCHANT_OrderWalletGetHandle *owgh)
319 : {
320 0 : if (NULL != owgh->job)
321 : {
322 0 : GNUNET_CURL_job_cancel (owgh->job);
323 0 : owgh->job = NULL;
324 : }
325 0 : GNUNET_free (owgh->url);
326 0 : GNUNET_free (owgh);
327 0 : }
328 :
329 :
330 : /* end of merchant_api_wallet_get_order.c */
|