merchant_api_post-private-orders-ORDER_ID-refund-external.c (7139B)
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-refund-external.c 19 * @brief Implementation of the POST /private/orders/$ORDER_ID/refund-external 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-refund-external.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/refund-external operation. 38 */ 39 struct TALER_MERCHANT_PostPrivateOrdersRefundExternalHandle 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_PostPrivateOrdersRefundExternalCallback cb; 60 61 /** 62 * Closure for @a cb. 63 */ 64 TALER_MERCHANT_POST_PRIVATE_ORDERS_REFUND_EXTERNAL_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 record the refund for. 78 */ 79 char *order_id; 80 81 /** 82 * External refund entry to record. 83 */ 84 json_t *body; 85 }; 86 87 88 /** 89 * Function called when we're done processing the 90 * HTTP POST /private/orders/$ORDER_ID/refund-external request. 91 * 92 * @param cls the `struct TALER_MERCHANT_PostPrivateOrdersRefundExternalHandle` 93 * @param response_code HTTP response code, 0 on error 94 * @param response response body, NULL if not in JSON 95 */ 96 static void 97 handle_refund_external_finished (void *cls, 98 long response_code, 99 const void *response) 100 { 101 struct TALER_MERCHANT_PostPrivateOrdersRefundExternalHandle *poreh = cls; 102 const json_t *json = response; 103 struct TALER_MERCHANT_PostPrivateOrdersRefundExternalResponse rer = { 104 .hr.http_status = (unsigned int) response_code, 105 .hr.reply = json 106 }; 107 108 poreh->job = NULL; 109 switch (response_code) 110 { 111 case 0: 112 rer.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 113 break; 114 case MHD_HTTP_OK: 115 { 116 struct GNUNET_JSON_Specification spec[] = { 117 GNUNET_JSON_spec_string ("refund_id", 118 &rer.details.ok.refund_id), 119 GNUNET_JSON_spec_end () 120 }; 121 122 if (GNUNET_OK != 123 GNUNET_JSON_parse (json, 124 spec, 125 NULL, 126 NULL)) 127 { 128 GNUNET_break_op (0); 129 rer.hr.http_status = 0; 130 rer.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 131 } 132 break; 133 } 134 case MHD_HTTP_BAD_REQUEST: 135 rer.hr.ec = TALER_JSON_get_error_code (json); 136 rer.hr.hint = TALER_JSON_get_error_hint (json); 137 break; 138 case MHD_HTTP_UNAUTHORIZED: 139 rer.hr.ec = TALER_JSON_get_error_code (json); 140 rer.hr.hint = TALER_JSON_get_error_hint (json); 141 break; 142 case MHD_HTTP_NOT_FOUND: 143 rer.hr.ec = TALER_JSON_get_error_code (json); 144 rer.hr.hint = TALER_JSON_get_error_hint (json); 145 break; 146 case MHD_HTTP_CONFLICT: 147 rer.hr.ec = TALER_JSON_get_error_code (json); 148 rer.hr.hint = TALER_JSON_get_error_hint (json); 149 break; 150 default: 151 GNUNET_break_op (0); 152 TALER_MERCHANT_parse_error_details_ (json, 153 response_code, 154 &rer.hr); 155 break; 156 } 157 poreh->cb (poreh->cb_cls, 158 &rer); 159 TALER_MERCHANT_post_private_orders_refund_external_cancel (poreh); 160 } 161 162 163 struct TALER_MERCHANT_PostPrivateOrdersRefundExternalHandle * 164 TALER_MERCHANT_post_private_orders_refund_external_create ( 165 struct GNUNET_CURL_Context *ctx, 166 const char *url, 167 const char *order_id, 168 const json_t *body) 169 { 170 struct TALER_MERCHANT_PostPrivateOrdersRefundExternalHandle *poreh; 171 172 poreh = GNUNET_new ( 173 struct TALER_MERCHANT_PostPrivateOrdersRefundExternalHandle); 174 poreh->ctx = ctx; 175 poreh->base_url = GNUNET_strdup (url); 176 poreh->order_id = GNUNET_strdup (order_id); 177 poreh->body = json_deep_copy (body); 178 GNUNET_assert (NULL != poreh->body); 179 return poreh; 180 } 181 182 183 enum TALER_ErrorCode 184 TALER_MERCHANT_post_private_orders_refund_external_start ( 185 struct TALER_MERCHANT_PostPrivateOrdersRefundExternalHandle *poreh, 186 TALER_MERCHANT_PostPrivateOrdersRefundExternalCallback cb, 187 TALER_MERCHANT_POST_PRIVATE_ORDERS_REFUND_EXTERNAL_RESULT_CLOSURE *cb_cls) 188 { 189 CURL *eh; 190 191 poreh->cb = cb; 192 poreh->cb_cls = cb_cls; 193 { 194 char *path; 195 196 GNUNET_asprintf (&path, 197 "private/orders/%s/refund-external", 198 poreh->order_id); 199 poreh->url = TALER_url_join (poreh->base_url, 200 path, 201 NULL); 202 GNUNET_free (path); 203 } 204 if (NULL == poreh->url) 205 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 206 eh = TALER_MERCHANT_curl_easy_get_ (poreh->url); 207 if ( (NULL == eh) || 208 (GNUNET_OK != 209 TALER_curl_easy_post (&poreh->post_ctx, 210 eh, 211 poreh->body)) ) 212 { 213 GNUNET_break (0); 214 if (NULL != eh) 215 curl_easy_cleanup (eh); 216 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 217 } 218 poreh->job = GNUNET_CURL_job_add2 (poreh->ctx, 219 eh, 220 poreh->post_ctx.headers, 221 &handle_refund_external_finished, 222 poreh); 223 if (NULL == poreh->job) 224 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 225 return TALER_EC_NONE; 226 } 227 228 229 void 230 TALER_MERCHANT_post_private_orders_refund_external_cancel ( 231 struct TALER_MERCHANT_PostPrivateOrdersRefundExternalHandle *poreh) 232 { 233 if (NULL != poreh->job) 234 { 235 GNUNET_CURL_job_cancel (poreh->job); 236 poreh->job = NULL; 237 } 238 TALER_curl_easy_post_finished (&poreh->post_ctx); 239 json_decref (poreh->body); 240 GNUNET_free (poreh->order_id); 241 GNUNET_free (poreh->url); 242 GNUNET_free (poreh->base_url); 243 GNUNET_free (poreh); 244 } 245 246 247 /* end of merchant_api_post-private-orders-ORDER_ID-refund-external.c */