Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2020-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,
11 : but 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
16 : Public License along with TALER; see the file COPYING.LGPL.
17 : If not, see <http://www.gnu.org/licenses/>
18 : */
19 : /**
20 : * @file merchant_api_post_order_paid.c
21 : * @brief Implementation of the POST /order/$ID/paid request
22 : * of the merchant's HTTP API
23 : * @author Jonathan Buchanan
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 "merchant_api_common.h"
34 : #include <taler/taler_json_lib.h>
35 : #include <taler/taler_signatures.h>
36 : #include <taler/taler_exchange_service.h>
37 : #include <taler/taler_curl_lib.h>
38 :
39 :
40 : /**
41 : * @brief Handle to a POST /orders/$ID/paid operation at a merchant.
42 : */
43 : struct TALER_MERCHANT_OrderPaidHandle
44 : {
45 :
46 : /**
47 : * The 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_OrderPaidCallback paid_cb;
60 :
61 : /**
62 : * Closure for @a paid_cb.
63 : */
64 : void *paid_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 :
78 : /**
79 : * Function called when we're done processing the
80 : * HTTP /paid request.
81 : *
82 : * @param cls the `struct TALER_MERCHANT_OrderPaidHandle`
83 : * @param response_code HTTP response code, 0 on error
84 : * @param response response body, NULL if not in JSON
85 : */
86 : static void
87 4 : handle_paid_finished (void *cls,
88 : long response_code,
89 : const void *response)
90 : {
91 4 : struct TALER_MERCHANT_OrderPaidHandle *oph = cls;
92 4 : const json_t *json = response;
93 4 : struct TALER_MERCHANT_OrderPaidResponse opr = {
94 4 : .hr.http_status = (unsigned int) response_code,
95 : .hr.reply = json
96 : };
97 :
98 4 : oph->job = NULL;
99 4 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
100 : "/paid completed with response code %u\n",
101 : (unsigned int) response_code);
102 4 : switch (response_code)
103 : {
104 0 : case 0:
105 0 : opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
106 0 : break;
107 4 : case MHD_HTTP_OK:
108 : {
109 : bool refunded;
110 : struct GNUNET_JSON_Specification spec[] = {
111 4 : GNUNET_JSON_spec_bool ("refunded",
112 : &refunded),
113 4 : GNUNET_JSON_spec_end ()
114 : };
115 :
116 4 : if (GNUNET_OK !=
117 4 : GNUNET_JSON_parse (opr.hr.reply,
118 : spec,
119 : NULL,
120 : NULL))
121 : {
122 0 : GNUNET_break_op (0);
123 0 : opr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
124 0 : break;
125 : }
126 4 : break;
127 : }
128 : break;
129 0 : case MHD_HTTP_BAD_REQUEST:
130 0 : opr.hr.ec = TALER_JSON_get_error_code (json);
131 0 : opr.hr.hint = TALER_JSON_get_error_hint (json);
132 : /* This should never happen, either us
133 : * or the merchant is buggy (or API version conflict);
134 : * just pass JSON reply to the application */
135 0 : break;
136 0 : case MHD_HTTP_FORBIDDEN:
137 0 : opr.hr.ec = TALER_JSON_get_error_code (json);
138 0 : opr.hr.hint = TALER_JSON_get_error_hint (json);
139 : /* The signature provided was invalid */
140 0 : break;
141 0 : case MHD_HTTP_NOT_FOUND:
142 0 : opr.hr.ec = TALER_JSON_get_error_code (json);
143 0 : opr.hr.hint = TALER_JSON_get_error_hint (json);
144 : /* Nothing really to verify, this should never
145 : happen, we should pass the JSON reply to the
146 : application */
147 0 : break;
148 0 : case MHD_HTTP_CONFLICT:
149 0 : opr.hr.ec = TALER_JSON_get_error_code (json);
150 0 : opr.hr.hint = TALER_JSON_get_error_hint (json);
151 : /* The hashed contract terms don't match with the order_id. */
152 0 : break;
153 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
154 0 : opr.hr.ec = TALER_JSON_get_error_code (json);
155 0 : opr.hr.hint = TALER_JSON_get_error_hint (json);
156 : /* Server had an internal issue; we should retry,
157 : but this API leaves this to the application */
158 0 : break;
159 0 : case MHD_HTTP_SERVICE_UNAVAILABLE:
160 0 : TALER_MERCHANT_parse_error_details_ (json,
161 : response_code,
162 : &opr.hr);
163 : /* Exchange couldn't respond properly; the retry is
164 : left to the application */
165 0 : break;
166 0 : default:
167 0 : TALER_MERCHANT_parse_error_details_ (json,
168 : response_code,
169 : &opr.hr);
170 : /* unexpected response code */
171 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
172 : "Unexpected response code %u/%d\n",
173 : (unsigned int) response_code,
174 : (int) opr.hr.ec);
175 0 : GNUNET_break_op (0);
176 0 : break;
177 : }
178 4 : oph->paid_cb (oph->paid_cb_cls,
179 : &opr);
180 4 : TALER_MERCHANT_order_paid_cancel (oph);
181 4 : }
182 :
183 :
184 : struct TALER_MERCHANT_OrderPaidHandle *
185 4 : TALER_MERCHANT_order_paid (
186 : struct GNUNET_CURL_Context *ctx,
187 : const char *merchant_url,
188 : const char *order_id,
189 : const char *session_id,
190 : const struct TALER_PrivateContractHashP *h_contract_terms,
191 : const struct GNUNET_HashCode *wallet_data_hash,
192 : const struct TALER_MerchantSignatureP *merchant_sig,
193 : TALER_MERCHANT_OrderPaidCallback paid_cb,
194 : void *paid_cb_cls)
195 : {
196 : struct TALER_MERCHANT_OrderPaidHandle *oph;
197 : json_t *req_obj;
198 :
199 4 : req_obj = GNUNET_JSON_PACK (
200 : GNUNET_JSON_pack_data_auto ("sig",
201 : merchant_sig),
202 : GNUNET_JSON_pack_data_auto ("h_contract",
203 : h_contract_terms),
204 : GNUNET_JSON_pack_allow_null (
205 : GNUNET_JSON_pack_data_auto ("wallet_data_hash",
206 : wallet_data_hash)),
207 : GNUNET_JSON_pack_string ("session_id",
208 : session_id));
209 4 : oph = GNUNET_new (struct TALER_MERCHANT_OrderPaidHandle);
210 4 : oph->ctx = ctx;
211 4 : oph->paid_cb = paid_cb;
212 4 : oph->paid_cb_cls = paid_cb_cls;
213 : {
214 : char *path;
215 :
216 4 : GNUNET_asprintf (&path,
217 : "orders/%s/paid",
218 : order_id);
219 4 : oph->url = TALER_url_join (merchant_url,
220 : path,
221 : NULL);
222 4 : GNUNET_free (path);
223 : }
224 4 : if (NULL == oph->url)
225 : {
226 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
227 : "Could not construct request URL.\n");
228 0 : json_decref (req_obj);
229 0 : GNUNET_free (oph);
230 0 : return NULL;
231 : }
232 : {
233 : CURL *eh;
234 :
235 4 : eh = TALER_MERCHANT_curl_easy_get_ (oph->url);
236 4 : if (GNUNET_OK !=
237 4 : TALER_curl_easy_post (&oph->post_ctx,
238 : eh,
239 : req_obj))
240 : {
241 0 : GNUNET_break (0);
242 0 : curl_easy_cleanup (eh);
243 0 : json_decref (req_obj);
244 0 : GNUNET_free (oph);
245 0 : return NULL;
246 : }
247 4 : json_decref (req_obj);
248 8 : oph->job = GNUNET_CURL_job_add2 (ctx,
249 : eh,
250 4 : oph->post_ctx.headers,
251 : &handle_paid_finished,
252 : oph);
253 : }
254 4 : return oph;
255 : }
256 :
257 :
258 : void
259 4 : TALER_MERCHANT_order_paid_cancel (
260 : struct TALER_MERCHANT_OrderPaidHandle *oph)
261 : {
262 4 : if (NULL != oph->job)
263 : {
264 0 : GNUNET_CURL_job_cancel (oph->job);
265 0 : oph->job = NULL;
266 : }
267 4 : TALER_curl_easy_post_finished (&oph->post_ctx);
268 4 : GNUNET_free (oph->url);
269 4 : GNUNET_free (oph);
270 4 : }
271 :
272 :
273 : /* end of merchant_api_post_order_paid.c */
|