merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit db7782aefcc036970b8bd6fbde87aef43740db37
parent 39a0a96415e81c4f2170d3d4efeb65a6f8b837c3
Author: Christian Grothoff <grothoff@gnunet.org>
Date:   Fri, 10 Jul 2026 08:47:07 +0200

clean up /abort library endpoint to match spec

Diffstat:
Msrc/include/taler/merchant/post-orders-ORDER_ID-abort.h | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Msrc/lib/merchant_api_post-orders-ORDER_ID-abort.c | 74+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
2 files changed, 120 insertions(+), 25 deletions(-)

diff --git a/src/include/taler/merchant/post-orders-ORDER_ID-abort.h b/src/include/taler/merchant/post-orders-ORDER_ID-abort.h @@ -62,11 +62,6 @@ struct TALER_MERCHANT_PostOrdersAbortCoin */ struct TALER_DenominationSignature denom_sig; - /** - * Amount contributed without the deposit fee. - */ - struct TALER_Amount contribution; - }; @@ -75,21 +70,73 @@ struct TALER_MERCHANT_PostOrdersAbortCoin */ struct TALER_MERCHANT_PostOrdersAbortedCoin { - /** - * Exchange signature confirming the refund. + * Type of status for this coin, determines content of @e details */ - struct TALER_ExchangeSignatureP exchange_sig; + enum + { + TALER_MERCHANT_POAC_UNDEPOSITED = 0, + TALER_MERCHANT_POAC_SUCCESS, + TALER_MERCHANT_POAC_FAILURE, + } type; /** - * Exchange public key used for @e exchange_sig. + * Public key of the coin. */ - struct TALER_ExchangePublicKeyP exchange_pub; + struct TALER_CoinSpendPublicKeyP coin_pub; /** - * Public key of the coin. + * Details depending on @e type */ - struct TALER_CoinSpendPublicKeyP coin_pub; + union + { + + /** + * Details on #TALER_MERCHANT_POAC_SUCCESS. + */ + struct + { + + /** + * Exchange signature confirming the refund. + */ + struct TALER_ExchangeSignatureP exchange_sig; + + /** + * Exchange public key used for @e exchange_sig. + */ + struct TALER_ExchangePublicKeyP exchange_pub; + + /** + * HTTP status of the abort from the exchange. + */ + uint32_t exchange_status; + } success; + + /** + * Details on #TALER_MERCHANT_POAC_FAILURE. + */ + struct + { + + /** + * HTTP status of the abort from the exchange. + */ + uint32_t exchange_status; + + /** + * Error code from the exchange. + */ + enum TALER_ErrorCode exchange_code; + + /** + * Full exchange reply, could be NULL. + */ + const json_t *exchange_reply; + + } failure; + + } details; }; diff --git a/src/lib/merchant_api_post-orders-ORDER_ID-abort.c b/src/lib/merchant_api_post-orders-ORDER_ID-abort.c @@ -150,34 +150,51 @@ check_abort_refund (struct TALER_MERCHANT_PostOrdersAbortHandle *poah, { struct TALER_MERCHANT_PostOrdersAbortedCoin res[GNUNET_NZL (num_refunds)]; + memset (res, + 0, + sizeof (res)); for (unsigned int i = 0; i<num_refunds; i++) { json_t *refund = json_array_get (refunds, i); - uint32_t exchange_status; - struct GNUNET_JSON_Specification spec_es[] = { - GNUNET_JSON_spec_uint32 ("exchange_status", - &exchange_status), + struct TALER_MERCHANT_PostOrdersAbortedCoin *ac = &res[i]; + const char *type; + struct GNUNET_JSON_Specification spec_type[] = { + GNUNET_JSON_spec_string ("type", + &type), GNUNET_JSON_spec_end () }; + ac->coin_pub = poah->coins[i].coin_pub; if (GNUNET_OK != GNUNET_JSON_parse (refund, - spec_es, + spec_type, NULL, NULL)) { GNUNET_break_op (0); return GNUNET_SYSERR; } - if (MHD_HTTP_OK == exchange_status) + if (0 == strcmp (type, + "undeposited")) + { + /* An "undeposited" entry (MerchantAbortPayRefundUndepositedStatus) + carries no exchange_status and no refund confirmation to verify. */ + ac->type = TALER_MERCHANT_POAC_UNDEPOSITED; + continue; + } + if (0 == strcmp (type, + "success")) { struct GNUNET_JSON_Specification spec_detail[] = { + GNUNET_JSON_spec_uint32 ("exchange_status", + &ac->details.success.exchange_status), GNUNET_JSON_spec_fixed_auto ("exchange_sig", - &res[i].exchange_sig), + &ac->details.success.exchange_sig), GNUNET_JSON_spec_fixed_auto ("exchange_pub", - &res[i].exchange_pub), + &ac->details.success.exchange_pub), GNUNET_JSON_spec_end () }; + ac->type = TALER_MERCHANT_POAC_SUCCESS; if (GNUNET_OK != GNUNET_JSON_parse (refund, spec_detail, @@ -186,7 +203,11 @@ check_abort_refund (struct TALER_MERCHANT_PostOrdersAbortHandle *poah, GNUNET_break_op (0); return GNUNET_SYSERR; } - res[i].coin_pub = poah->coins[i].coin_pub; + if (MHD_HTTP_OK != ac->details.success.exchange_status) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } if (GNUNET_OK != TALER_exchange_online_refund_confirmation_verify ( @@ -195,13 +216,42 @@ check_abort_refund (struct TALER_MERCHANT_PostOrdersAbortHandle *poah, &poah->merchant_pub, 0, /* transaction id */ &poah->coins[i].amount_with_fee, - &res[i].exchange_pub, - &res[i].exchange_sig)) + &ac->details.success.exchange_pub, + &ac->details.success.exchange_sig)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + continue; + } + if (0 == strcmp (type, + "failure")) + { + struct GNUNET_JSON_Specification spec_detail[] = { + GNUNET_JSON_spec_uint32 ("exchange_status", + &ac->details.failure.exchange_status), + TALER_JSON_spec_ec ("exchange_code", + &ac->details.failure.exchange_code), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_object_const ("exchange_reply", + &ac->details.failure.exchange_reply), + NULL), + GNUNET_JSON_spec_end () + }; + + ac->type = TALER_MERCHANT_POAC_FAILURE; + if (GNUNET_OK != + GNUNET_JSON_parse (refund, + spec_detail, + NULL, NULL)) { GNUNET_break_op (0); return GNUNET_SYSERR; } + continue; } + GNUNET_break_op (0); + return GNUNET_SYSERR; } switch (ar->hr.http_status) { @@ -394,8 +444,6 @@ TALER_MERCHANT_post_orders_abort_start ( j_coin = GNUNET_JSON_PACK ( GNUNET_JSON_pack_data_auto ("coin_pub", &ac->coin_pub), - TALER_JSON_pack_amount ("contribution", - &ac->amount_with_fee), GNUNET_JSON_pack_string ("exchange_url", ac->exchange_url)); if (0 !=