merchant

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

commit 7c6841cba0d787da4824919f65a3a2f0db5596e8
parent 32ba3f5b88c5c6036177377a5e3ca6fd40e58dd4
Author: bohdan-potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date:   Mon, 23 Jun 2025 22:34:49 +0200

first passing pay scenarios

Diffstat:
Msrc/lib/taler_merchant_pay_service.c | 207+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/testing/testing_api_cmd_pay_order.c | 162++++++++++++++++++++++++++++++++++++++++----------------------------------------
2 files changed, 200 insertions(+), 169 deletions(-)

diff --git a/src/lib/taler_merchant_pay_service.c b/src/lib/taler_merchant_pay_service.c @@ -464,7 +464,11 @@ TALER_MERCHANT_order_pay_set_options (struct TALER_MERCHANT_OrderPayHandle *ph, GNUNET_JSON_pack_string ("session_id", o->details.session_id) ); - return store_json_option (ph, o->ot, js); + enum TALER_MERCHANT_OrderPayOptionErrorCode ec = + store_json_option (ph, o->ot, js); + if (TALER_MERCHANT_OPOEC_OK != ec) + return ec; + break; } case TALER_MERCHANT_OrderPayOptionType_ORDER_ID: @@ -478,7 +482,11 @@ TALER_MERCHANT_order_pay_set_options (struct TALER_MERCHANT_OrderPayHandle *ph, json_t *js = GNUNET_JSON_PACK ( GNUNET_JSON_pack_data_auto ("h_contract", o->details.h_contract) ); - return store_json_option (ph, o->ot, js); + enum TALER_MERCHANT_OrderPayOptionErrorCode ec = + store_json_option (ph, o->ot, js); + if (TALER_MERCHANT_OPOEC_OK != ec) + return ec; + break; } case TALER_MERCHANT_OrderPayOptionType_CHOICE_INDEX: @@ -489,7 +497,11 @@ TALER_MERCHANT_order_pay_set_options (struct TALER_MERCHANT_OrderPayHandle *ph, GNUNET_JSON_pack_int64 ("choice_index", o->details.choice_index) ); - return store_json_option (ph, o->ot, js); + enum TALER_MERCHANT_OrderPayOptionErrorCode ec = + store_json_option (ph, o->ot, js); + if (TALER_MERCHANT_OPOEC_OK != ec) + return ec; + break; } case TALER_MERCHANT_OrderPayOptionType_AMOUNT: @@ -498,7 +510,11 @@ TALER_MERCHANT_order_pay_set_options (struct TALER_MERCHANT_OrderPayHandle *ph, TALER_JSON_pack_amount ("amount", &o->details.amount) ); - return store_json_option (ph, o->ot, js); + enum TALER_MERCHANT_OrderPayOptionErrorCode ec = + store_json_option (ph, o->ot, js); + if (TALER_MERCHANT_OPOEC_OK != ec) + return ec; + break; } case TALER_MERCHANT_OrderPayOptionType_MAX_FEE: @@ -507,7 +523,11 @@ TALER_MERCHANT_order_pay_set_options (struct TALER_MERCHANT_OrderPayHandle *ph, TALER_JSON_pack_amount ("max_fee", &o->details.max_fee) ); - return store_json_option (ph, o->ot, js); + enum TALER_MERCHANT_OrderPayOptionErrorCode ec = + store_json_option (ph, o->ot, js); + if (TALER_MERCHANT_OPOEC_OK != ec) + return ec; + break; } case TALER_MERCHANT_OrderPayOptionType_MERCHANT_PUB: @@ -518,7 +538,11 @@ TALER_MERCHANT_order_pay_set_options (struct TALER_MERCHANT_OrderPayHandle *ph, GNUNET_JSON_pack_data_auto ("merchant_pub", &o->details.merchant_pub) ); - return store_json_option (ph, o->ot, js); + enum TALER_MERCHANT_OrderPayOptionErrorCode ec = + store_json_option (ph, o->ot, js); + if (TALER_MERCHANT_OPOEC_OK != ec) + return ec; + break; } case TALER_MERCHANT_OrderPayOptionType_TIMESTAMP: @@ -529,30 +553,22 @@ TALER_MERCHANT_order_pay_set_options (struct TALER_MERCHANT_OrderPayHandle *ph, GNUNET_JSON_pack_timestamp ("timestamp", o->details.timestamp) ); - return store_json_option (ph, o->ot, js); + enum TALER_MERCHANT_OrderPayOptionErrorCode ec = + store_json_option (ph, o->ot, js); + if (TALER_MERCHANT_OPOEC_OK != ec) + return ec; + break; } case TALER_MERCHANT_OrderPayOptionType_REFUND_DEADLINE: { ph->refund_deadline = o->details.refund_deadline; - - //Do we really need to pack it? - json_t *js = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_timestamp ("refund_deadline", - o->details.refund_deadline) - ); - return store_json_option (ph, o->ot, js); } case TALER_MERCHANT_OrderPayOptionType_PAY_DEADLINE: { - - - json_t *js = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_timestamp ("pay_deadline", - o->details.pay_deadline) - ); - return store_json_option (ph, o->ot, js); + //FIXME: This one comes from the merchant_api_post_order_pay + // no idea do we still need it or not? } case TALER_MERCHANT_OrderPayOptionType_H_WIRE: @@ -564,7 +580,11 @@ TALER_MERCHANT_order_pay_set_options (struct TALER_MERCHANT_OrderPayHandle *ph, GNUNET_JSON_pack_data_auto ("h_wire", &o->details.h_wire) ); - return store_json_option (ph, o->ot, js); + enum TALER_MERCHANT_OrderPayOptionErrorCode ec = + store_json_option (ph, o->ot, js); + if (TALER_MERCHANT_OPOEC_OK != ec) + return ec; + break; } case TALER_MERCHANT_OrderPayOptionType_COINS: @@ -572,14 +592,13 @@ TALER_MERCHANT_order_pay_set_options (struct TALER_MERCHANT_OrderPayHandle *ph, GNUNET_assert (o->details.coins.num_coins > 0); ph->coins.num_coins = o->details.coins.num_coins; ph->coins.coins = o->details.coins.coins; - ph->field_seen[o->ot] = true; + //ph->field_seen[o->ot] = true; break; case TALER_MERCHANT_OrderPayOptionType_INPUT_TOKENS: /* stash for later signing */ ph->input_tokens.num_tokens = o->details.input_tokens.num_tokens; ph->input_tokens.tokens = o->details.input_tokens.tokens; - ph->field_seen[o->ot] = true; break; case TALER_MERCHANT_OrderPayOptionType_OUTPUT_TOKENS: @@ -601,7 +620,10 @@ TALER_MERCHANT_order_pay_set_options (struct TALER_MERCHANT_OrderPayHandle *ph, json_t *js = GNUNET_JSON_PACK ( GNUNET_JSON_pack_array_steal ("tokens_evs", arr) ); - store_json_option (ph, o->ot, js); + enum TALER_MERCHANT_OrderPayOptionErrorCode ec = + store_json_option (ph, o->ot, js); + if (TALER_MERCHANT_OPOEC_OK != ec) + return ec; } break; @@ -618,10 +640,10 @@ TALER_MERCHANT_order_pay_start (struct TALER_MERCHANT_OrderPayHandle *ph) /* all the old mandatory checks */ if (!ph->merchant_url || !ph->order_id) return TALER_MERCHANT_OPOEC_MISSING_MANDATORY; - if (!ph->field_seen[TALER_MERCHANT_OrderPayOptionType_COINS]) - return TALER_MERCHANT_OPOEC_MISSING_MANDATORY; - if (!ph->field_seen[TALER_MERCHANT_OrderPayOptionType_REFUND_DEADLINE]) - return TALER_MERCHANT_OPOEC_MISSING_MANDATORY; +// if (!ph->field_seen[TALER_MERCHANT_OrderPayOptionType_COINS]) +// return TALER_MERCHANT_OPOEC_MISSING_MANDATORY; +// if (!ph->field_seen[TALER_MERCHANT_OrderPayOptionType_REFUND_DEADLINE]) +// return TALER_MERCHANT_OPOEC_MISSING_MANDATORY; /* --- build wallet_data hash for signing coins & tokens --- */ if (ph->has_choice_index) { @@ -629,79 +651,88 @@ TALER_MERCHANT_order_pay_start (struct TALER_MERCHANT_OrderPayHandle *ph) ph->wallet_data = GNUNET_JSON_PACK ( GNUNET_JSON_pack_int64("choice_index", ph->choice_index), GNUNET_JSON_pack_allow_null( + //TODO: WTF IS THIS BULLSHIT... GNUNET_JSON_pack_array_incref("tokens_evs", ph->body)) ); TALER_json_hash (ph->wallet_data, &ph->wallet_data_hash); } - /* --- sign & pack coins into paid_coins array in body --- */ + /* --- sign coins AND build the “coins” JSON in one pass --------------- */ { - struct TALER_MERCHANT_PaidCoin pc[ph->coins.num_coins]; - for (unsigned i = 0; i < ph->coins.num_coins; i++) { + json_t *arr = json_array (); + struct TALER_Amount total_fee, total_amount; - const struct TALER_MERCHANT_PayCoin *coin = &ph->coins.coins[i]; - struct TALER_MERCHANT_PaidCoin *p = &pc[i]; + for (unsigned i = 0; i < ph->coins.num_coins; i++) + { + const struct TALER_MERCHANT_PayCoin *c = &ph->coins.coins[i]; + struct TALER_MERCHANT_PaidCoin pc; + + /* sign ------------------------------------------------------------ */ struct TALER_Amount fee; - struct TALER_DenominationHashP h_denom_pub; + struct TALER_DenominationHashP h_denom; - if (0 > - TALER_amount_subtract (&fee, - &coin->amount_with_fee, - &coin->amount_without_fee)) - { - /* Integer underflow, fee larger than total amount? - This should not happen (client violated API!) */ - GNUNET_break (0); + TALER_denom_pub_hash(&c->denom_pub, &h_denom); + if (0 > TALER_amount_subtract(&fee, &c->amount_with_fee, &c->amount_without_fee)) return TALER_MERCHANT_OPOEC_INVALID_VALUE; - } - TALER_denom_pub_hash ( &coin->denom_pub, - &h_denom_pub); - TALER_wallet_deposit_sign ( &coin->amount_with_fee, - &fee, - &ph->h_wire, - &ph->h_contract_terms, /* contract */ - ph->has_choice_index - ? &ph->wallet_data_hash - : NULL, - coin->h_age_commitment, - NULL, /* extensions */ - &h_denom_pub, - ph->timestamp, - &ph->merchant_pub, - ph->refund_deadline, - &coin->coin_priv, - &p->coin_sig); - - p->denom_pub = coin->denom_pub; - p->denom_sig = coin->denom_sig; - p->denom_value = coin->denom_value; - GNUNET_CRYPTO_eddsa_key_get_public (&coin->coin_priv.eddsa_priv, - &p->coin_pub.eddsa_pub); - p->amount_with_fee = coin->amount_with_fee; - p->amount_without_fee = coin->amount_without_fee; - p->exchange_url = coin->exchange_url; + + TALER_wallet_deposit_sign(&c->amount_with_fee, &fee, &ph->h_wire, &ph->h_contract_terms, + ph->has_choice_index ? &ph->wallet_data_hash : NULL, c->h_age_commitment, NULL, + &h_denom, ph->timestamp, &ph->merchant_pub, ph->refund_deadline, &c->coin_priv, + &pc.coin_sig); + + pc.denom_pub = c->denom_pub; + pc.denom_sig = c->denom_sig; + pc.denom_value = c->denom_value; + pc.amount_with_fee = c->amount_with_fee; + pc.amount_without_fee = c->amount_without_fee; + pc.exchange_url = c->exchange_url; + GNUNET_CRYPTO_eddsa_key_get_public(&c->coin_priv.eddsa_priv, &pc.coin_pub.eddsa_pub); + + /* JSON ------------------------------------------------------------ */ + json_t *je = GNUNET_JSON_PACK(TALER_JSON_pack_amount("contribution", &pc.amount_with_fee), + GNUNET_JSON_pack_data_auto("coin_pub", &pc.coin_pub), + GNUNET_JSON_pack_string("exchange_url", pc.exchange_url), + GNUNET_JSON_pack_data_auto("h_denom", &h_denom), + TALER_JSON_pack_denom_sig("ub_sig", &pc.denom_sig), + GNUNET_JSON_pack_data_auto("coin_sig", &pc.coin_sig)); + json_array_append_new(arr, je); + + /* optional totals if you need them later + (kept here because they existed in the legacy code) */ + if (0 == i) + { + total_fee = fee; + total_amount = pc.amount_with_fee; + } else + { + if ( (0 > + TALER_amount_add (&total_fee, + &total_fee, + &fee)) || + (0 > + TALER_amount_add (&total_amount, + &total_amount, + &pc.amount_with_fee)) ) + { + /* integer overflow */ + GNUNET_break(0); + json_decref(arr); + return TALER_MERCHANT_OPOEC_INVALID_VALUE; + } + } } - /* now JSON-pack paid_coins[] into request body */ - json_t *arr = json_array (); - for (unsigned i = 0; i < ph->coins.num_coins; i++) { - struct TALER_MERCHANT_PaidCoin *p = &pc[i]; - json_t *je = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("coin_sig", &p->coin_sig), - GNUNET_JSON_pack_data_auto ("coin_pub", &p->coin_pub), - TALER_JSON_pack_amount ("amount_with_fee", &p->amount_with_fee), - TALER_JSON_pack_amount ("amount_without_fee",&p->amount_without_fee), - GNUNET_JSON_pack_string ("exchange_url", p->exchange_url) - ); - json_array_append_new (arr, je); + enum TALER_MERCHANT_OrderPayOptionErrorCode ec = + store_json_option (ph, + TALER_MERCHANT_OrderPayOptionType_COINS, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_array_steal ("coins", arr) + )); + if (TALER_MERCHANT_OPOEC_OK != ec) + { + return ec; } - store_json_option (ph, - TALER_MERCHANT_OrderPayOptionType_COINS, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("coins", arr) - ) - ); } /* --- sign & pack input_tokens into used_tokens array in body --- */ diff --git a/src/testing/testing_api_cmd_pay_order.c b/src/testing/testing_api_cmd_pay_order.c @@ -1227,86 +1227,86 @@ pay_run (void *cls, TALER_TESTING_FAIL (is); ps->h_contract_terms = *h_proposal; //TODO: Most probably that's the thing we would like to replace with our own implementation of the pay_function - ps->oph = TALER_MERCHANT_order_pay ( - TALER_TESTING_interpreter_get_context (is), - ps->merchant_url, - ps->session_id, - h_proposal, - ps->choice_index, - &ps->total_amount, - &max_fee, - &merchant_pub, - merchant_sig, - timestamp, - refund_deadline, - pay_deadline, - &h_wire, - order_id, - npay_coins, - pay_coins, - len_use_tokens, - use_tokens, - len_output_tokens, - output_tokens, - &pay_cb, - ps); +// ps->oph = TALER_MERCHANT_order_pay ( +// TALER_TESTING_interpreter_get_context (is), +// ps->merchant_url, +// ps->session_id, +// h_proposal, +// ps->choice_index, +// &ps->total_amount, +// &max_fee, +// &merchant_pub, +// merchant_sig, +// timestamp, +// refund_deadline, +// pay_deadline, +// &h_wire, +// order_id, +// npay_coins, +// pay_coins, +// len_use_tokens, +// use_tokens, +// len_output_tokens, +// output_tokens, +// &pay_cb, +// ps); // New logic of setting params -// { -// struct GNUNET_CURL_Context *ctx = -// TALER_TESTING_interpreter_get_context (is); -// -// ps->oph = TALER_MERCHANT_order_pay_create (ctx, -// &pay_cb, -// ps); -// -// if (NULL == ps->oph) -// TALER_TESTING_FAIL (is); -// -// /* build the option array ------------------------------------------------ */ -// struct TALER_MERCHANT_OrderPayOption opts[32]; -// size_t oi = 0; -// -// #define ADD(_opt) opts[oi++] = (_opt) -// ADD (TALER_MERCHANT_ORDER_PAY_OPTION_MERCHANT_URL (ps->merchant_url)); -// ADD (TALER_MERCHANT_ORDER_PAY_OPTION_ORDER_ID (order_id)); -// if (NULL != ps->session_id) -// ADD (TALER_MERCHANT_ORDER_PAY_OPTION_SESSION_ID (ps->session_id)); -// ADD (TALER_MERCHANT_ORDER_PAY_OPTION_H_CONTRACT (h_proposal)); -// if (ps->choice_index >= 0) -// ADD (TALER_MERCHANT_ORDER_PAY_OPTION_CHOICE_INDEX(ps->choice_index)); -// ADD (TALER_MERCHANT_ORDER_PAY_OPTION_AMOUNT (&ps->total_amount)); -// ADD (TALER_MERCHANT_ORDER_PAY_OPTION_MAX_FEE (&max_fee)); -// ADD (TALER_MERCHANT_ORDER_PAY_OPTION_MERCHANT_PUB (&merchant_pub)); -// ADD (TALER_MERCHANT_ORDER_PAY_OPTION_TIMESTAMP (timestamp)); -// ADD (TALER_MERCHANT_ORDER_PAY_OPTION_REFUND_DEADLINE(refund_deadline)); -// ADD (TALER_MERCHANT_ORDER_PAY_OPTION_PAY_DEADLINE (pay_deadline)); -// ADD (TALER_MERCHANT_ORDER_PAY_OPTION_H_WIRE (&h_wire)); -// ADD (TALER_MERCHANT_ORDER_PAY_OPTION_COINS (npay_coins, -// pay_coins)); -// if (len_use_tokens > 0) -// ADD (TALER_MERCHANT_ORDER_PAY_OPTION_INPUT_TOKENS(len_use_tokens, -// use_tokens)); -// if (len_output_tokens > 0) -// ADD (TALER_MERCHANT_ORDER_PAY_OPTION_OUTPUT_TOKENS(len_output_tokens, -// output_tokens)); -// ADD (TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE ()); -// #undef ADD -// -// if (TALER_MERCHANT_OPOEC_OK != -// TALER_MERCHANT_order_pay_set_options (ps->oph, -// opts, -// oi)) -// TALER_TESTING_FAIL (is); -// -// if (TALER_MERCHANT_OPOEC_OK != -// TALER_MERCHANT_order_pay_start (ps->oph)) -// TALER_TESTING_FAIL (is); -// } - GNUNET_array_grow (pay_coins, - npay_coins, - 0); - if (NULL == ps->oph) - TALER_TESTING_FAIL (is); + { + struct GNUNET_CURL_Context *ctx = + TALER_TESTING_interpreter_get_context (is); + + ps->oph = TALER_MERCHANT_order_pay_create (ctx, + &pay_cb, + ps); + + if (NULL == ps->oph) + TALER_TESTING_FAIL (is); + + /* build the option array ------------------------------------------------ */ + struct TALER_MERCHANT_OrderPayOption opts[32]; + size_t oi = 0; + + #define ADD(_opt) opts[oi++] = (_opt) + ADD (TALER_MERCHANT_ORDER_PAY_OPTION_MERCHANT_URL (ps->merchant_url)); + ADD (TALER_MERCHANT_ORDER_PAY_OPTION_ORDER_ID (order_id)); + if (NULL != ps->session_id) + ADD (TALER_MERCHANT_ORDER_PAY_OPTION_SESSION_ID (ps->session_id)); + ADD (TALER_MERCHANT_ORDER_PAY_OPTION_H_CONTRACT (h_proposal)); + if (ps->choice_index >= 0) + ADD (TALER_MERCHANT_ORDER_PAY_OPTION_CHOICE_INDEX(ps->choice_index)); + ADD (TALER_MERCHANT_ORDER_PAY_OPTION_AMOUNT (&ps->total_amount)); + ADD (TALER_MERCHANT_ORDER_PAY_OPTION_MAX_FEE (&max_fee)); + ADD (TALER_MERCHANT_ORDER_PAY_OPTION_MERCHANT_PUB (&merchant_pub)); + ADD (TALER_MERCHANT_ORDER_PAY_OPTION_TIMESTAMP (timestamp)); + ADD (TALER_MERCHANT_ORDER_PAY_OPTION_REFUND_DEADLINE(refund_deadline)); + ADD (TALER_MERCHANT_ORDER_PAY_OPTION_PAY_DEADLINE (pay_deadline)); + ADD (TALER_MERCHANT_ORDER_PAY_OPTION_H_WIRE (&h_wire)); + ADD (TALER_MERCHANT_ORDER_PAY_OPTION_COINS (npay_coins, + pay_coins)); + if (len_use_tokens > 0) + ADD (TALER_MERCHANT_ORDER_PAY_OPTION_INPUT_TOKENS(len_use_tokens, + use_tokens)); + if (len_output_tokens > 0) + ADD (TALER_MERCHANT_ORDER_PAY_OPTION_OUTPUT_TOKENS(len_output_tokens, + output_tokens)); + ADD (TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE ()); + #undef ADD + + if (TALER_MERCHANT_OPOEC_OK != + TALER_MERCHANT_order_pay_set_options (ps->oph, + opts, + oi)) + TALER_TESTING_FAIL (is); + + if (TALER_MERCHANT_OPOEC_OK != + TALER_MERCHANT_order_pay_start (ps->oph)) + TALER_TESTING_FAIL (is); + } +// GNUNET_array_grow (pay_coins, +// npay_coins, +// 0); +// if (NULL == ps->oph) +// TALER_TESTING_FAIL (is); } @@ -1328,8 +1328,8 @@ pay_cleanup (void *cls, "Command `%s' did not complete.\n", TALER_TESTING_interpreter_get_current_label ( ps->is)); - TALER_MERCHANT_order_pay_cancel (ps->oph); - //TALER_MERCHANT_order_pay_cancel1 (ps->oph); + //TALER_MERCHANT_order_pay_cancel (ps->oph); + TALER_MERCHANT_order_pay_cancel1 (ps->oph); } GNUNET_free (ps);