merchant

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

commit 1ae1dcbbbba24b3135659dd8b38b9dc1c392f515
parent 9e2c0eef5cbead66bce50a9e0ef55cce0e440484
Author: Christian Blättler <blatc2@bfh.ch>
Date:   Thu,  7 Mar 2024 17:02:59 +0100

add choices array to v0 contract logic

Diffstat:
Msrc/backend/taler-merchant-httpd_contract.h | 2+-
Msrc/backend/taler-merchant-httpd_private-post-orders.c | 367+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
2 files changed, 278 insertions(+), 91 deletions(-)

diff --git a/src/backend/taler-merchant-httpd_contract.h b/src/backend/taler-merchant-httpd_contract.h @@ -237,7 +237,7 @@ struct TALER_MerchantContractChoice /** * Array of products that are part of the purchase. */ - json_t *products; + const json_t *products; /** * List of inputs the wallet must provision (all of them) to satisfy the diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c @@ -225,7 +225,142 @@ struct OrderContext /** * Information set in the ORDER_PHASE_PARSE_ORDER phase. */ - struct TALER_MerchantContract parse_order; + struct + { + /** + * Our order ID. + */ + const char *order_id; + + /** + * Array of possible specific contracts the wallet/customer may choose + * from by selecting the respective index when signing the deposit + * confirmation. + */ + struct TALER_MerchantContractChoice *choices; + + /** + * Length of the @e choices array. + */ + unsigned int choices_len; + + /** + * Summary of the order. + */ + // const char *summary; + + /** + * Internationalized summary. + */ + // json_t *summary_i18n; + + /** + * URL where the same contract could be ordered again (if available). + */ + const char *public_reorder_url; + + /** + * URL that will show that the order was successful + * after it has been paid for. + */ + // const char *fulfillment_url; + + /** + * Message shown to the customer after paying for the order. + * Either fulfillment_url or fulfillment_message must be specified. + */ + // const char *fulfillment_message; + + /** + * Map from IETF BCP 47 language tags to localized fulfillment messages. + */ + // json_t *fulfillment_message_i18n; + + /** + * Merchant base URL. + */ + char *merchant_base_url; + + /** + * Timestamp of the order. + */ + struct GNUNET_TIME_Timestamp timestamp; + + /** + * Deadline for refunds. + */ + struct GNUNET_TIME_Timestamp refund_deadline; + + /** + * Payment deadline. + */ + struct GNUNET_TIME_Timestamp pay_deadline; + + /** + * Wire transfer deadline. + */ + struct GNUNET_TIME_Timestamp wire_deadline; + + /** + * Delivery date. + */ + struct GNUNET_TIME_Timestamp delivery_date; + + /** + * Delivery location. + */ + json_t *delivery_location; + + /** + * Array of products that are part of the purchase. + */ + // const json_t *products; + + /** + * TODO: Maybe remove this and set it from settings where we serialize + * the order to JSON? + * + * Information like name, website, email, etc. about the merchant. + */ + json_t *merchant; + + /** + * TODO: Maybe remove this and set it from settings where we serialize + * the order to JSON? + * + * Merchant's public key + */ + struct TALER_MerchantPublicKeyP merchant_pub; + + /** + * Gross amount value of the contract. Used to + * compute @e max_stefan_fee. + */ + struct TALER_Amount brutto; + + /** + * Maximum fee as given by the client request. + */ + struct TALER_Amount max_fee; + + /** + * Specifies for how long the wallet should try to get an + * automatic refund for the purchase. + */ + struct GNUNET_TIME_Relative auto_refund; + + /** + * Nonce generated by the wallet and echoed by the merchant + * in this field when the proposal is generated. + */ + const char *nonce; + + /** + * Extra data that is only interpreted by the merchant frontend. + */ + const json_t *extra; + + } parse_order; /** * Information set in the ORDER_PHASE_MERGE_INVENTORY phase. @@ -547,7 +682,7 @@ execute_transaction (struct OrderContext *oc) &oc->parse_request.h_post_data, oc->parse_order.pay_deadline, &oc->parse_request.claim_token, - oc->serialize_order.contract, /* called 'contract terms' in database. */ + oc->serialize_order.contract, /* called 'contract terms' at database. */ oc->parse_request.pos_key, oc->parse_request.pos_algorithm); if (qs <= 0) @@ -1139,19 +1274,90 @@ get_exchange_keys (void *cls, static void serialize_order (struct OrderContext *oc) { - const struct TALER_MERCHANTDB_InstanceSettings *settings = - &oc->hc->instance->settings; - enum GNUNET_GenericReturnValue ret; - - ret = TMH_serialize_contract( - oc->parse_order, - oc->hc->instance, - oc->add_payment_details.wm, - oc->set_exchanges.exchanges, - oc->merge_inventory.products, - oc->set_max_fee.max_fee, - oc->serialize_order.contract - ); + struct TALER_MerchantContractChoice *choice = oc->parse_order.choices; + + GNUNET_assert (1 == oc->parse_order.choices_len && NULL != choice); + + oc->serialize_order.contract = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("summary", + choice->summary), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("summary_i18n", + choice->summary_i18n)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("public_reorder_url", + oc->parse_order.public_reorder_url)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("fulfillment_message", + choice->fulfillment_message)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("fulfillment_message_i18n", + choice->fulfillment_message_i18n)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("fulfillment_url", + choice->fulfillment_url)), + GNUNET_JSON_pack_array_incref ("products", + oc->merge_inventory.products), + GNUNET_JSON_pack_data_auto ("h_wire", + &oc->add_payment_details.wm->h_wire), + GNUNET_JSON_pack_string ("wire_method", + oc->add_payment_details.wm->wire_method), + GNUNET_JSON_pack_string ("order_id", + oc->parse_order.order_id), + GNUNET_JSON_pack_timestamp ("timestamp", + oc->parse_order.timestamp), + GNUNET_JSON_pack_timestamp ("pay_deadline", + oc->parse_order.pay_deadline), + GNUNET_JSON_pack_timestamp ("wire_transfer_deadline", + oc->parse_order.wire_deadline), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_timestamp ("delivery_date", + oc->parse_order.delivery_date)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("delivery_location", + oc->parse_order.delivery_location)), + GNUNET_JSON_pack_string ("merchant_base_url", + oc->parse_order.merchant_base_url), + GNUNET_JSON_pack_object_incref ("merchant", + oc->parse_order.merchant), + GNUNET_JSON_pack_data_auto ("merchant_pub", + &oc->hc->instance->merchant_pub), + GNUNET_JSON_pack_array_incref ("exchanges", + oc->set_exchanges.exchanges), + TALER_JSON_pack_amount ("max_fee", + &oc->set_max_fee.max_fee), + TALER_JSON_pack_amount ("amount", + &oc->parse_order.brutto), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("extra", + (json_t *) oc->parse_order.extra)) + ); + + /* Pack does not work here, because it doesn't set zero-values for timestamps */ + GNUNET_assert (0 == + json_object_set_new (oc->serialize_order.contract, + "refund_deadline", + GNUNET_JSON_from_timestamp ( + oc->parse_order.refund_deadline))); + GNUNET_log ( + GNUNET_ERROR_TYPE_INFO, + "Refund deadline for contact is %llu\n", + (unsigned long long) oc->parse_order.refund_deadline.abs_time.abs_value_us); + GNUNET_log ( + GNUNET_ERROR_TYPE_INFO, + "Wallet timestamp for contact is %llu\n", + (unsigned long long) oc->parse_order.timestamp.abs_time.abs_value_us); + + /* Pack does not work here, because it sets zero-values for relative times */ + /* auto_refund should only be set if it is not 0 */ + if (! GNUNET_TIME_relative_is_zero (oc->parse_order.auto_refund)) + { + GNUNET_assert (0 == + json_object_set_new (oc->serialize_order.contract, + "auto_refund", + GNUNET_JSON_from_time_rel ( + oc->parse_order.auto_refund))); + } oc->phase++; } @@ -1252,6 +1458,7 @@ set_exchanges (struct OrderContext *oc) return false; } + /** * Add missing fields to the order. Upon success, continue * processing with merge_inventory(). @@ -1261,57 +1468,6 @@ set_exchanges (struct OrderContext *oc) static void parse_order (struct OrderContext *oc) { - const char *version = NULL; - - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("version", - &version), - NULL), - GNUNET_JSON_spec_end () - }; - enum GNUNET_GenericReturnValue ret; - - ret = TALER_MHD_parse_json_data (oc->connection, - oc->parse_request.order, - spec); - - if (GNUNET_OK != ret) - { - GNUNET_break_op (0); - finalize_order2 (oc, - ret); - return; - } - - if (0 == strcmp("v0", version)) - { - oc->parse_order.version = TALER_MCV_V0; - } - else if (0 != strcmp("v1", version)) - { - oc->parse_order.version = TALER_MCV_V1; - } - else - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_VERSION_MALFORMED, - "invalid version specified in order, supported are 'v0' or 'v1'"); - return; - } -} - -/** - * Parse order fields if order.version is null or 0. - * - * @param[in,out] oc order context - */ -static void -parse_order_v0 (struct OrderContext *oc) -{ struct TALER_MerchantContractChoice *choice; struct TALER_MerchantContractLimits *limits; @@ -1384,7 +1540,7 @@ parse_order_v0 (struct OrderContext *oc) NULL), GNUNET_JSON_spec_mark_optional ( TALER_JSON_spec_amount_any ("max_fee", - &limits->max_fee), + &oc->parse_order.max_fee), &no_fee), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_json ("delivery_location", @@ -1432,7 +1588,7 @@ parse_order_v0 (struct OrderContext *oc) if ( (! no_fee) && (GNUNET_OK != TALER_amount_cmp_currency (&oc->parse_order.brutto, - &limits->max_fee)) ) + &oc->parse_order.max_fee)) ) { GNUNET_break_op (0); GNUNET_JSON_parse_free (spec); @@ -1443,11 +1599,6 @@ parse_order_v0 (struct OrderContext *oc) return; } - GNUNET_snprintf (limits->currency, - sizeof (limits->currency), - "%s", - oc->parse_order.brutto.currency); - /* Add order_id if it doesn't exist. */ if (NULL == oc->parse_order.order_id) { @@ -1700,6 +1851,53 @@ parse_order_v0 (struct OrderContext *oc) return; } + { + oc->parse_order.merchant = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("name", + settings->name), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("website", + settings->website)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("email", + settings->email)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("logo", + settings->logo))); + GNUNET_assert (NULL != oc->parse_order.merchant); + { + json_t *loca; + + /* Handle merchant address */ + loca = settings->address; + if (NULL != loca) + { + loca = json_deep_copy (loca); + GNUNET_assert (NULL != loca); + GNUNET_assert (0 == + json_object_set_new (oc->parse_order.merchant, + "address", + loca)); + } + } + { + json_t *juri; + + /* Handle merchant jurisdiction */ + juri = settings->jurisdiction; + if (NULL != juri) + { + juri = json_deep_copy (juri); + GNUNET_assert (NULL != juri); + GNUNET_assert (0 == + json_object_set_new (oc->parse_order.merchant, + "jurisdiction", + juri)); + } + } + } + + oc->parse_order.merchant_pub = oc->hc->instance->merchant_pub; if ( (NULL != oc->parse_order.delivery_location) && (! TMH_location_object_valid (oc->parse_order.delivery_location)) ) { @@ -1711,24 +1909,9 @@ parse_order_v0 (struct OrderContext *oc) return; } - oc->parse_order.choices = choice; - oc->parse_order.choices_len = 1; - oc->parse_order.limits = limits; - oc->parse_order.limits_len = 1; - oc->phase++; } -/** - * Parse order fields if order.version is 1. - * - * @param[in,out] oc order context - */ -static void -parse_order_v1 (struct OrderContext *oc) -{ - -} /** * Process the @a payment_target and add the details of how the @@ -1776,13 +1959,17 @@ add_payment_details (struct OrderContext *oc) static void merge_inventory (struct OrderContext *oc) { + struct TALER_MerchantContractChoice *choice = oc->parse_order.choices; + + GNUNET_assert (1 == oc->parse_order.choices_len && NULL != choice); + /** * parse_request.inventory_products => instructions to add products to contract terms * parse_order.products => contains products that are not from the backend-managed inventory. */ - if (NULL != oc->parse_order.products) + if (NULL != choice->products) oc->merge_inventory.products - = json_deep_copy (oc->parse_order.products); + = json_deep_copy (choice->products); else oc->merge_inventory.products = json_array ();