/* This file is part of TALER (C) 2024 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. TALER is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with TALER; see the file COPYING. If not, see */ /** * @file taler-merchant-httpd_contract.h * @brief shared logic for contract terms handling * @author Christian Blättler */ #include "taler-merchant-httpd.h" #include #include #include /** * Possible versions of the contract terms. */ enum TALER_MerchantContractVersion { /** * Version 0 */ TALER_MCV_V0 = 0, /** * Version 1 */ TALER_MCV_V1 = 1 }; /** * Possible token kinds. */ enum TALER_MerchantContractTokenKind { /** * Subscription token kind */ TALER_MCTK_SUBSCRIPTION = 0, /** * Discount token kind */ TALER_MCTK_DISCOUNT = 1 }; /** * Possible input types for the contract terms. */ enum TALER_MerchantContractInputType { /** * Input type invalid */ TALER_MCIT_INVALID = 0, /** * Input type coin */ TALER_MCIT_COIN = 1, /** * Input type token */ TALER_MCIT_TOKEN = 2 }; /** * Contract input (part of the v1 contract terms). */ struct TALER_MerchantContractInput { /** * Type of the input. */ enum TALER_MerchantContractInputType type; union { /** * Coin-based input (ration). (Future work, only here for reference) */ // struct // { // /** // * Price to be paid. // */ // struct TALER_Amount price; // /** // * Base URL of the ration authority. // */ // const char *ration_authority_url; // } coin; /** * Token-based input. */ struct { /** * Slug of the token family to be used. */ const char *token_family_slug; /** * Start time of the validity period of the token. Base on this timestamp * the wallet can find the correct key for this token in token_authorities. */ struct GNUNET_TIME_Timestamp valid_after; /** * Number of tokens of this type required. Defaults to one if the * field is not provided. */ unsigned int count; } token; } details; }; /** * Possible output types for the contract terms. */ enum TALER_MerchantContractOutputType { /** * Invalid output type */ TALER_MCOT_INVALID = 0, /** * Output type coin */ TALER_MCOT_COIN = 1, /** * Output type token */ TALER_MCOT_TOKEN = 2, /** * Output type tax-receipt */ TALER_MCOT_TAX_RECEIPT = 3 }; /** * Contract output (part of the v1 contract terms). */ struct TALER_MerchantContractOutput { /** * Type of the output. */ enum TALER_MerchantContractOutputType type; union { /** * Coin-based output. */ struct { /** * Coins that will be yielded. This excludes any applicable withdraw fees. */ struct TALER_Amount brutto_yield; /** * Base URL of the exchange that will issue the coins. */ const char *exchange_url; } coin; /** * Tax-receipt output. */ struct { /** * Base URL of the donation authority that will issue the tax receipt. */ const char *donau_url; } tax_receipt; /** * Token-based output. */ struct { /** * Slug of the token family to be issued. */ const char *token_family_slug; /** * Start time of the validity period of the token. Base on this timestamp * the wallet can find the correct key for this token in token_authorities. */ struct GNUNET_TIME_Timestamp valid_after; /** * Number of tokens of this type required. Defaults to one if the * field is not provided. */ unsigned int count; } token; } details; }; /** * Contract choice (part of the v1 contract terms). */ struct TALER_MerchantContractChoice { /** * List of inputs the wallet must provision (all of them) to satisfy the * conditions for the contract. */ struct TALER_MerchantContractInput *inputs; /** * Length of the @e inputs array. */ unsigned int inputs_len; /** * List of outputs the merchant promises to yield (all of them) once * the contract is paid. */ struct TALER_MerchantContractOutput *outputs; /** * Length of the @e outputs array. */ unsigned int outputs_len; }; /** * Public key and corresponding metadata for a token family. */ struct TALER_MerchantContractTokenFamilyKey { /** * Public key. */ struct TALER_TokenIssuePublicKeyP pub; /** * Tokens signed by this key will be valid after this time. */ struct GNUNET_TIME_Timestamp valid_after; /** * Tokens signed by this key will be valid before this time. */ struct GNUNET_TIME_Timestamp valid_before; }; struct TALER_MerchantContractTokenFamily { /** * Slug of the token family. */ const char *slug; /** * Human-readable name of the token family. */ char *name; /** * Human-readable description of the semantics of the tokens issued by * this token family. */ char *description; /** * Map from IETF BCP 47 language tags to localized description. */ json_t *description_i18n; /** * Relevant public keys of this token family for the given contract. */ struct TALER_MerchantContractTokenFamilyKey *keys; /** * Length of the @e keys array. */ unsigned int keys_len; /** * Must a wallet understand this token type to process contracts that * consume or yield it? */ bool critical; /** * Kind of the token family. */ enum TALER_MerchantContractTokenKind kind; /** * Kind-specific information about the token. */ union { /** * Subscription token. */ struct { /** * Array of domain names where this subscription can be safely used * (e.g. the issuer warrants that these sites will re-issue tokens of * this type if the respective contract says so). May contain "*" for * any domain or subdomain. */ const char **trusted_domains; /** * Length of the @e trusted_domains array. */ unsigned int trusted_domains_len; } subscription; /** * Discount token. */ struct { /** * Array of domain names where this discount token is intended to be * used. May contain "*" for any domain or subdomain. Users should be * warned about sites proposing to consume discount tokens of this * type that are not in this list that the merchant is accepting a * coupon from a competitor and thus may be attaching different * semantics (like get 20% discount for my competitors 30% discount * token). */ const char **expected_domains; /** * Length of the @e expected_domains array. */ unsigned int expected_domains_len; } discount; } details; }; /** * Struct to hold contract terms in v0 and v1 format. v0 contracts are modelled * as a v1 contract with a single choice and no inputs and outputs. Use the * version field to explicitly differentiate between v0 and v1 contracts. */ struct TALER_MerchantContract { /** * URL where the same contract could be ordered again (if available). */ const char *public_reorder_url; /** * Our order ID. */ const char *order_id; /** * Merchant base URL. */ char *merchant_base_url; /** * Merchant information. */ struct { /** * Legal name of the instance */ char *name; /** * Merchant's site url */ char *website; /** * Email contact for customers */ char *email; /** * merchant's logo data uri */ char *logo; /** * Merchant address */ json_t *address; /** * Jurisdiction of the business */ json_t *jurisdiction; } merchant; /** * Price to be paid for the transaction. Could be 0. The price is in addition * to other instruments, such as rations and tokens. * The exchange will subtract deposit fees from that amount * before transferring it to the merchant. */ struct TALER_Amount brutto; /** * Summary of the contract. */ const char *summary; /** * Internationalized summary. */ json_t *summary_i18n; /** * URL that will show that the contract was successful * after it has been paid for. */ const char *fulfillment_url; /** * Message shown to the customer after paying for the contract. * 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; /** * Array of products that are part of the purchase. */ const json_t *products; /** * Timestamp of the contract. */ struct GNUNET_TIME_Timestamp timestamp; /** * Deadline for refunds. */ struct GNUNET_TIME_Timestamp refund_deadline; /** * Specifies for how long the wallet should try to get an * automatic refund for the purchase. */ struct GNUNET_TIME_Relative auto_refund; /** * 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; /** * 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; /** * Specified version of the contract. */ enum TALER_MerchantContractVersion version; /** * 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; /** * Array of token authorities. */ struct TALER_MerchantContractTokenFamily *token_authorities; /** * Length of the @e token_authorities array. */ unsigned int token_authorities_len; /** * Maximum fee as given by the client request. */ struct TALER_Amount max_fee; // TODO: Add exchanges array }; enum TALER_MerchantContractInputType TMH_contract_input_type_from_string (const char *str); enum TALER_MerchantContractOutputType TMH_contract_output_type_from_string (const char *str); const char * TMH_string_from_contract_input_type (enum TALER_MerchantContractInputType t); const char * TMH_string_from_contract_output_type (enum TALER_MerchantContractOutputType t); /** * Serialize @a contract to a JSON object, ready to be stored in the database. * The @a contract can be of v0 or v1. * * @param[in] contract contract struct to serialize * @param[in] instance merchant instance for this contract * @param[in] exchanges JSON array of exchanges * @param[out] out serialized contract as JSON object * @return #GNUNET_OK on success * #GNUNET_NO if @a contract was not valid * #GNUNET_SYSERR on failure */ enum GNUNET_GenericReturnValue TMH_serialize_contract (const struct TALER_MerchantContract *contract, const struct TMH_MerchantInstance *instance, json_t *exchanges, json_t **out); enum GNUNET_GenericReturnValue TMH_serialize_contract_v0 (const struct TALER_MerchantContract *contract, const struct TMH_MerchantInstance *instance, json_t *exchanges, json_t **out); enum GNUNET_GenericReturnValue TMH_serialize_contract_v1 (const struct TALER_MerchantContract *contract, const struct TMH_MerchantInstance *instance, json_t *exchanges, json_t **out); /** * Provide specification to parse given JSON array to an array * of contract choices. * * @param name name of the choices field in the JSON * @param[out] choices pointer to the first element of the array * @param[out] choices_len pointer to the length of the array * @return spec for parsing a choices array */ struct GNUNET_JSON_Specification TALER_JSON_spec_choices (const char *name, struct TALER_MerchantContractChoice **choices, unsigned int *choices_len); /** * Provide specification to parse given JSON object to an array * of token families. * * @param name name of the token families field in the JSON * @param[out] families pointer to the first element of the array * @param[out] families_len pointer to the length of the array * @return spec for parsing a token families object */ struct GNUNET_JSON_Specification TALER_JSON_spec_token_families (const char *name, struct TALER_MerchantContractTokenFamily **families, unsigned int *families_len); /** * Find matching token family in @a families based on @a slug. Then use * @a valid_after to find the matching public key within it. * * @param slug slug of the token family * @param valid_after start time of the validity period of the key * @param families array of token families to search in * @param families_len length of the @a families array * @param[out] family found family, set to NULL to only check for existence * @param[out] key found key, set to NULL to only check for existence * @return #GNUNET_OK on success #GNUNET_NO if no key was found */ enum GNUNET_GenericReturnValue TMH_find_token_family_key (const char *slug, struct GNUNET_TIME_Timestamp valid_after, struct TALER_MerchantContractTokenFamily *families, unsigned int families_len, struct TALER_MerchantContractTokenFamily *family, struct TALER_MerchantContractTokenFamilyKey *key);