merchant-0036-init.sql.fragment (81615B)
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 General Public License as published by the Free Software 7 -- Foundation; either version 3, 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 General Public License for more details. 12 -- 13 -- You should have received a copy of the GNU General Public License along with 14 -- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 16 -- @file merchant-0036-init.sql.fragment 17 -- @brief Create initial set of per-instance tables and indices 18 -- @author Christian Grothoff 19 20 CREATE PROCEDURE merchant.merchant_0036_init(s TEXT) 21 LANGUAGE plpgsql 22 AS $OUTER$ 23 BEGIN 24 -- All object creation below uses unqualified names that resolve against 25 -- the per-instance schema `s`. Cross-schema references (the merchant.* 26 -- types, the global merchant.* tables and the public.* operator classes) 27 -- stay explicitly qualified. Tables are created in FK-dependency order so 28 -- that foreign keys can be declared inline with the CREATE TABLE. 29 EXECUTE format('SET LOCAL search_path TO %I', s); 30 31 -- =================================================================== 32 -- Tier 0: tables without intra-schema foreign keys. 33 -- =================================================================== 34 35 CREATE TABLE merchant_accounts ( 36 account_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 37 h_wire BYTEA NOT NULL UNIQUE, 38 salt BYTEA NOT NULL, 39 credit_facade_url TEXT, 40 credit_facade_credentials JSONB, 41 last_bank_serial INT8 DEFAULT 0 NOT NULL, 42 payto_uri TEXT NOT NULL UNIQUE, 43 active BOOLEAN NOT NULL, 44 extra_wire_subject_metadata TEXT, 45 CONSTRAINT merchant_accounts_h_wire_check CHECK ((LENGTH(h_wire) = 64)), 46 CONSTRAINT merchant_accounts_salt_check CHECK ((LENGTH(salt) = 16)) 47 ); 48 COMMENT ON TABLE merchant_accounts IS 'bank accounts of the instances'; 49 COMMENT ON COLUMN merchant_accounts.account_serial IS 'Unique identifier for this account, used to reference this account from other tables'; 50 COMMENT ON COLUMN merchant_accounts.h_wire IS 'salted hash of payto_uri'; 51 COMMENT ON COLUMN merchant_accounts.salt IS 'salt used when hashing payto_uri into h_wire'; 52 COMMENT ON COLUMN merchant_accounts.credit_facade_url IS 'Base URL of a facade where the merchant can inquire about incoming bank transactions into this account'; 53 COMMENT ON COLUMN merchant_accounts.credit_facade_credentials IS 'JSON with credentials needed to access the credit facade'; 54 COMMENT ON COLUMN merchant_accounts.last_bank_serial IS 'Serial number of the bank of the last transaction we successfully imported'; 55 COMMENT ON COLUMN merchant_accounts.payto_uri IS 'payto URI of a merchant bank account'; 56 COMMENT ON COLUMN merchant_accounts.active IS 'true if we actively use this bank account, false if it is just kept around for older contracts to refer to'; 57 COMMENT ON COLUMN merchant_accounts.extra_wire_subject_metadata IS 'Additional meta data that the exchange should include in wire transfer subjects made to this account on behalf of this instance'; 58 59 CREATE TABLE merchant_categories ( 60 category_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 61 category_name TEXT NOT NULL UNIQUE, 62 category_name_i18n JSONB NOT NULL 63 ); 64 COMMENT ON TABLE merchant_categories IS 'product categories (with translations) to group products from inventory (primarily for the point-of-sale app)'; 65 COMMENT ON COLUMN merchant_categories.category_name IS 'name of the category'; 66 COMMENT ON COLUMN merchant_categories.category_name_i18n IS 'JSON with translations of the category name'; 67 CREATE INDEX trgm_idx_categories_by_name 68 ON merchant_categories USING gin (lower(category_name) public.gin_trgm_ops); 69 70 CREATE TABLE merchant_contract_terms ( 71 order_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 72 order_id TEXT NOT NULL UNIQUE, 73 contract_terms JSONB NOT NULL, 74 wallet_data TEXT, 75 h_contract_terms BYTEA NOT NULL UNIQUE, 76 creation_time INT8 NOT NULL, 77 pay_deadline INT8 NOT NULL, 78 refund_deadline INT8 NOT NULL, 79 paid BOOLEAN DEFAULT FALSE NOT NULL, 80 wired BOOLEAN DEFAULT FALSE NOT NULL, 81 fulfillment_url TEXT, 82 session_id TEXT DEFAULT ''::text NOT NULL, 83 pos_key TEXT, 84 pos_algorithm INT4 DEFAULT 0 NOT NULL, 85 claim_token BYTEA NOT NULL, 86 choice_index INT2, 87 CONSTRAINT merchant_contract_terms_claim_token_check CHECK ((LENGTH(claim_token) = 16)), 88 CONSTRAINT merchant_contract_terms_h_contract_terms_check CHECK ((LENGTH(h_contract_terms) = 64)) 89 ); 90 COMMENT ON TABLE merchant_contract_terms IS 'Contracts are orders that have been claimed by a wallet'; 91 COMMENT ON COLUMN merchant_contract_terms.order_id IS 'Not a foreign key into merchant_orders because paid contracts persist after expiration'; 92 COMMENT ON COLUMN merchant_contract_terms.contract_terms IS 'These contract terms include the wallet nonce'; 93 COMMENT ON COLUMN merchant_contract_terms.wallet_data IS 'Data provided by the wallet when paying for the contract (subcontract selection, blinded tokens, etc.)'; 94 COMMENT ON COLUMN merchant_contract_terms.h_contract_terms IS 'Hash over contract_terms'; 95 COMMENT ON COLUMN merchant_contract_terms.creation_time IS 'time at which the order was originally created'; 96 COMMENT ON COLUMN merchant_contract_terms.pay_deadline IS 'How long is the offer valid. After this time, the order can be garbage collected'; 97 COMMENT ON COLUMN merchant_contract_terms.refund_deadline IS 'By what times do refunds have to be approved (useful to reject refund requests)'; 98 COMMENT ON COLUMN merchant_contract_terms.paid IS 'true implies the customer paid for this contract; order should be DELETEd from merchant_orders once paid is set to release merchant_order_locks; paid remains true even if the payment was later refunded'; 99 COMMENT ON COLUMN merchant_contract_terms.wired IS 'true implies the exchange wired us the full amount for all non-refunded payments under this contract'; 100 COMMENT ON COLUMN merchant_contract_terms.fulfillment_url IS 'also included in contract_terms, but we need it here to SELECT on it during repurchase detection; can be NULL if the contract has no fulfillment URL'; 101 COMMENT ON COLUMN merchant_contract_terms.session_id IS 'last session_id from we confirmed the paying client to use, empty string for none'; 102 COMMENT ON COLUMN merchant_contract_terms.pos_key IS 'enconded based key which is used for the verification of payment'; 103 COMMENT ON COLUMN merchant_contract_terms.pos_algorithm IS 'specifies the algorithm used to compute the verification code (usually some OTP variant)'; 104 COMMENT ON COLUMN merchant_contract_terms.claim_token IS 'Token optionally used to access the status of the order. All zeros (not NULL) if not used'; 105 COMMENT ON COLUMN merchant_contract_terms.choice_index IS 'Index of selected choice. Refers to the `choices` array in the contract terms. NULL for contracts without choices.'; 106 CREATE INDEX merchant_contract_terms_by_expiration 107 ON merchant_contract_terms (paid, pay_deadline); 108 COMMENT ON INDEX merchant_contract_terms_by_expiration IS 'for unlock_contracts'; 109 CREATE INDEX merchant_contract_terms_by_merchant_and_expiration 110 ON merchant_contract_terms (pay_deadline); 111 COMMENT ON INDEX merchant_contract_terms_by_merchant_and_expiration IS 'for delete_contract_terms'; 112 CREATE INDEX merchant_contract_terms_by_merchant_and_payment 113 ON merchant_contract_terms (paid); 114 CREATE INDEX merchant_contract_terms_by_merchant_and_session 115 ON merchant_contract_terms (session_id); 116 CREATE INDEX merchant_contract_terms_by_merchant_session_and_fulfillment 117 ON merchant_contract_terms (fulfillment_url, session_id); 118 CREATE INDEX trgm_idx_contract_summaries 119 ON merchant_contract_terms USING gin (lower((contract_terms ->> 'summary'::text)) public.gin_trgm_ops); 120 121 CREATE TABLE merchant_custom_units ( 122 unit_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 123 unit TEXT NOT NULL UNIQUE, 124 unit_name_long TEXT NOT NULL, 125 unit_name_short TEXT NOT NULL, 126 unit_name_long_i18n BYTEA DEFAULT convert_to('{}'::text, 'UTF8'::name) NOT NULL, 127 unit_name_short_i18n BYTEA DEFAULT convert_to('{}'::text, 'UTF8'::name) NOT NULL, 128 unit_allow_fraction BOOLEAN DEFAULT FALSE NOT NULL, 129 unit_precision_level INT4 DEFAULT 0 NOT NULL, 130 unit_active BOOLEAN DEFAULT TRUE NOT NULL, 131 CONSTRAINT merchant_custom_units_unit_precision_level_check 132 CHECK (((unit_precision_level >= 0) AND (unit_precision_level <= 6))) 133 ); 134 COMMENT ON TABLE merchant_custom_units IS 'Per-instance custom measurement units.'; 135 COMMENT ON COLUMN merchant_custom_units.unit IS 'FIXME'; 136 COMMENT ON COLUMN merchant_custom_units.unit_name_long IS 'FIXME'; 137 COMMENT ON COLUMN merchant_custom_units.unit_name_short IS 'FIXME'; 138 COMMENT ON COLUMN merchant_custom_units.unit_name_long_i18n IS 'FIXME'; 139 COMMENT ON COLUMN merchant_custom_units.unit_name_short_i18n IS 'FIXME'; 140 COMMENT ON COLUMN merchant_custom_units.unit_allow_fraction IS 'FIXME'; 141 COMMENT ON COLUMN merchant_custom_units.unit_precision_level IS 'FIXME'; 142 COMMENT ON COLUMN merchant_custom_units.unit_active IS 'FIXME'; 143 144 CREATE TABLE merchant_donau_instances ( 145 donau_instances_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 146 donau_url TEXT NOT NULL, 147 charity_name TEXT NOT NULL, 148 charity_id INT8 NOT NULL, 149 charity_max_per_year merchant.taler_amount_currency NOT NULL, 150 charity_receipts_to_date merchant.taler_amount_currency NOT NULL, 151 current_year INT8 NOT NULL, 152 UNIQUE (donau_url, charity_id) 153 ); 154 COMMENT ON TABLE merchant_donau_instances IS 'Here we store information about individual Donau instances, including details about associated charities and donation limits'; 155 COMMENT ON COLUMN merchant_donau_instances.donau_instances_serial IS 'Unique serial identifier for each Donau instance'; 156 COMMENT ON COLUMN merchant_donau_instances.donau_url IS 'The URL associated with the Donau system for this instance'; 157 COMMENT ON COLUMN merchant_donau_instances.charity_name IS 'Name of the charity (for humans)'; 158 COMMENT ON COLUMN merchant_donau_instances.charity_id IS 'The unique identifier for the charity organization linked to this Donau instance'; 159 COMMENT ON COLUMN merchant_donau_instances.charity_max_per_year IS 'Maximum allowable donation amount per year for the charity associated with this instance, stored in taler_amount_currency'; 160 COMMENT ON COLUMN merchant_donau_instances.charity_receipts_to_date IS 'The total amount of donations received to date for this instance, stored in taler_amount_currency'; 161 COMMENT ON COLUMN merchant_donau_instances.current_year IS 'The current year for tracking donations for this instance, stored as an 8-byte integer'; 162 163 CREATE TABLE merchant_login_tokens ( 164 serial INT8 GENERATED ALWAYS AS IDENTITY PRIMARY KEY, 165 token BYTEA NOT NULL UNIQUE, 166 creation_time INT8 NOT NULL, 167 expiration_time INT8 NOT NULL, 168 validity_scope INT4 NOT NULL, 169 description TEXT NOT NULL, 170 CONSTRAINT merchant_login_tokens_token_check CHECK ((LENGTH(token) = 32)) 171 ); 172 COMMENT ON TABLE merchant_login_tokens IS 'login tokens that have been created for the given instance'; 173 COMMENT ON COLUMN merchant_login_tokens.token IS 'binary value of the login token'; 174 COMMENT ON COLUMN merchant_login_tokens.creation_time IS 'time when the token was created; currently not used, potentially useful in the future for a forced logout of all tokens issued before a certain date'; 175 COMMENT ON COLUMN merchant_login_tokens.expiration_time IS 'determines when the token expires'; 176 COMMENT ON COLUMN merchant_login_tokens.validity_scope IS 'identifies the operations for which the token is valid'; 177 COMMENT ON COLUMN merchant_login_tokens.description IS 'Description of the login token'; 178 CREATE INDEX merchant_login_tokens_by_expiration 179 ON merchant_login_tokens (expiration_time); 180 181 CREATE TABLE merchant_money_pots ( 182 money_pot_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 183 money_pot_name TEXT NOT NULL UNIQUE, 184 money_pot_description TEXT NOT NULL, 185 pot_totals merchant.taler_amount_currency[] 186 DEFAULT ARRAY[]::merchant.taler_amount_currency[] NOT NULL 187 ); 188 COMMENT ON TABLE merchant_money_pots IS 'Accounting construct for tracking finances by groups such as net income, taxes, tips to be paid to staff, etc.'; 189 COMMENT ON COLUMN merchant_money_pots.money_pot_serial IS 'Unique identifier for the money pot'; 190 COMMENT ON COLUMN merchant_money_pots.money_pot_name IS 'Name for the money pot'; 191 COMMENT ON COLUMN merchant_money_pots.money_pot_description IS 'Human-readable description for the money pot'; 192 COMMENT ON COLUMN merchant_money_pots.pot_totals IS 'Total amounts in the pot'; 193 194 CREATE TABLE merchant_orders ( 195 order_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 196 order_id TEXT NOT NULL UNIQUE, 197 claim_token BYTEA NOT NULL, 198 h_post_data BYTEA NOT NULL, 199 pay_deadline INT8 NOT NULL, 200 creation_time INT8 NOT NULL, 201 contract_terms JSONB NOT NULL, 202 pos_key TEXT, 203 pos_algorithm INT4 DEFAULT 0 NOT NULL, 204 fulfillment_url TEXT, 205 session_id TEXT DEFAULT ''::text NOT NULL, 206 CONSTRAINT merchant_orders_claim_token_check CHECK ((LENGTH(claim_token) = 16)), 207 CONSTRAINT merchant_orders_h_post_data_check CHECK ((LENGTH(h_post_data) = 64)) 208 ); 209 COMMENT ON TABLE merchant_orders IS 'Orders we offered to a customer, but that have not yet been claimed'; 210 COMMENT ON COLUMN merchant_orders.order_id IS 'Unique identifier for the order'; 211 COMMENT ON COLUMN merchant_orders.claim_token IS 'Token optionally used to authorize the wallet to claim the order. All zeros (not NULL) if not used'; 212 COMMENT ON COLUMN merchant_orders.h_post_data IS 'Hash of the POST request that created this order, for idempotency checks'; 213 COMMENT ON COLUMN merchant_orders.pay_deadline IS 'How long is the offer valid. After this time, the order can be garbage collected'; 214 COMMENT ON COLUMN merchant_orders.creation_time IS 'time at which the order was originally created'; 215 COMMENT ON COLUMN merchant_orders.contract_terms IS 'Claiming changes the contract_terms, hence we have no hash of the terms in this table'; 216 COMMENT ON COLUMN merchant_orders.pos_key IS 'encoded based key which is used for the verification of payment'; 217 COMMENT ON COLUMN merchant_orders.pos_algorithm IS 'algorithm to used to generate the confirmation code. It is link with the pos_key'; 218 COMMENT ON COLUMN merchant_orders.fulfillment_url IS 'URL where the wallet will redirect the user upon payment'; 219 COMMENT ON COLUMN merchant_orders.session_id IS 'session_id to which the payment will be bound'; 220 CREATE INDEX merchant_orders_by_creation_time 221 ON merchant_orders (creation_time); 222 CREATE INDEX merchant_orders_by_expiration 223 ON merchant_orders (pay_deadline); 224 CREATE INDEX merchant_orders_by_merchant_and_fullfilment_and_session 225 ON merchant_orders (fulfillment_url, session_id); 226 CREATE INDEX merchant_orders_by_merchant_and_session 227 ON merchant_orders (session_id); 228 CREATE INDEX trgm_idx_order_summaries 229 ON merchant_orders USING gin (lower((contract_terms ->> 'summary'::text)) public.gin_trgm_ops); 230 231 CREATE TABLE merchant_otp_devices ( 232 otp_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 233 otp_id TEXT NOT NULL UNIQUE, 234 otp_description TEXT NOT NULL, 235 otp_key TEXT, 236 otp_algorithm INT4 DEFAULT 0 NOT NULL, 237 otp_ctr INT8 DEFAULT 0 NOT NULL 238 ); 239 COMMENT ON TABLE merchant_otp_devices IS 'OTP device owned by a merchant'; 240 COMMENT ON COLUMN merchant_otp_devices.otp_id IS 'slug identifying the OTP device in protocols'; 241 COMMENT ON COLUMN merchant_otp_devices.otp_description IS 'Human-readable OTP device description'; 242 COMMENT ON COLUMN merchant_otp_devices.otp_key IS 'A base64-encoded key of the point-of-sale. It will be use by the OTP device'; 243 COMMENT ON COLUMN merchant_otp_devices.otp_algorithm IS 'algorithm to used to generate the confirmation code. It is linked with the otp_key and otp_ctr'; 244 COMMENT ON COLUMN merchant_otp_devices.otp_ctr IS 'counter for counter-based OTP generators'; 245 246 CREATE TABLE merchant_reports ( 247 report_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 248 report_program_section TEXT NOT NULL, 249 report_description TEXT NOT NULL, 250 mime_type TEXT NOT NULL, 251 report_token BYTEA NOT NULL, 252 data_source TEXT NOT NULL, 253 target_address TEXT NOT NULL, 254 frequency INT8 NOT NULL, 255 frequency_shift INT8 NOT NULL, 256 next_transmission INT8 NOT NULL, 257 last_error_code INT4, 258 last_error_detail TEXT, 259 one_shot_hidden BOOLEAN DEFAULT FALSE, 260 CONSTRAINT merchant_reports_report_token_check CHECK ((LENGTH(report_token) = 32)) 261 ); 262 COMMENT ON TABLE merchant_reports IS 'Specifies where we should send periodic reports about instance activities'; 263 COMMENT ON COLUMN merchant_reports.report_serial IS 'Unique identifier for the report'; 264 COMMENT ON COLUMN merchant_reports.report_program_section IS 'Which helper program (configuration section) to use to transmit the report'; 265 COMMENT ON COLUMN merchant_reports.report_description IS 'FIXME'; 266 COMMENT ON COLUMN merchant_reports.mime_type IS 'Mime-type to request from the backend for the transmission'; 267 COMMENT ON COLUMN merchant_reports.report_token IS 'Token clients requesting the report must include in the /report request'; 268 COMMENT ON COLUMN merchant_reports.data_source IS 'Relative URL of the instance for a GET request to request data to send'; 269 COMMENT ON COLUMN merchant_reports.target_address IS 'Address to which the report should be sent'; 270 COMMENT ON COLUMN merchant_reports.frequency IS 'Relative time with the desired report frequency'; 271 COMMENT ON COLUMN merchant_reports.frequency_shift IS 'Relative time by which to offset the actual transmission from the frequency multiple'; 272 COMMENT ON COLUMN merchant_reports.next_transmission IS 'Absolute time at which we should do the next transmission'; 273 COMMENT ON COLUMN merchant_reports.last_error_code IS 'ErrorCode of the last attempted transmission, NULL on success'; 274 COMMENT ON COLUMN merchant_reports.last_error_detail IS 'Additional human-readable text explaining errors from the last transmission attempt (for diagnostics), NULL on success'; 275 COMMENT ON COLUMN merchant_reports.one_shot_hidden IS 'True for reports that are only supposed to trigger once and that should be hidden from the user interface as they are internally generated. Used for alerts, such as those generated when the KYC status changes.'; 276 277 CREATE TABLE merchant_product_groups ( 278 product_group_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 279 product_group_name TEXT NOT NULL UNIQUE, 280 product_group_description TEXT NOT NULL 281 ); 282 COMMENT ON TABLE merchant_product_groups IS 'Specifies a product group'; 283 COMMENT ON COLUMN merchant_product_groups.product_group_serial IS 'Unique identifier for the group'; 284 COMMENT ON COLUMN merchant_product_groups.product_group_name IS 'Name for the group'; 285 COMMENT ON COLUMN merchant_product_groups.product_group_description IS 'Human-readable description for the group'; 286 287 CREATE TABLE merchant_statistic_bucket_meta ( 288 bmeta_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 289 slug TEXT NOT NULL, 290 description TEXT NOT NULL, 291 stype merchant.statistic_type NOT NULL, 292 ranges merchant.statistic_range[] NOT NULL, 293 ages INT4[] NOT NULL, 294 CONSTRAINT equal_array_LENGTH CHECK ((array_LENGTH(ranges, 1) = array_LENGTH(ages, 1))), 295 UNIQUE (slug, stype) 296 ); 297 COMMENT ON TABLE merchant_statistic_bucket_meta IS 'meta data about a statistic with events falling into buckets we are tracking'; 298 COMMENT ON COLUMN merchant_statistic_bucket_meta.bmeta_serial_id IS 'unique identifier for this type of bucket statistic we are tracking'; 299 COMMENT ON COLUMN merchant_statistic_bucket_meta.slug IS 'keyword (or name) of the statistic; identifies what the statistic is about; should be a slug suitable for a URI path'; 300 COMMENT ON COLUMN merchant_statistic_bucket_meta.description IS 'description of the statistic being tracked'; 301 COMMENT ON COLUMN merchant_statistic_bucket_meta.stype IS 'statistic type, what kind of data is being tracked, amount or number'; 302 COMMENT ON COLUMN merchant_statistic_bucket_meta.ranges IS 'size of the buckets that are being kept for this statistic'; 303 COMMENT ON COLUMN merchant_statistic_bucket_meta.ages IS 'determines how long into the past we keep buckets for the range at the given index around (in generations)'; 304 305 CREATE TABLE merchant_statistic_interval_meta ( 306 imeta_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 307 slug TEXT NOT NULL, 308 description TEXT NOT NULL, 309 stype merchant.statistic_type NOT NULL, 310 ranges INT8[] NOT NULL, 311 precisions INT8[] NOT NULL, 312 CONSTRAINT equal_array_LENGTH CHECK ((array_LENGTH(ranges, 1) = array_LENGTH(precisions, 1))), 313 CONSTRAINT merchant_statistic_interval_meta_precisions_check CHECK ((array_LENGTH(precisions, 1) > 0)), 314 CONSTRAINT merchant_statistic_interval_meta_ranges_check CHECK ((array_LENGTH(ranges, 1) > 0)), 315 UNIQUE (slug, stype) 316 ); 317 COMMENT ON TABLE merchant_statistic_interval_meta IS 'meta data about an interval statistic we are tracking'; 318 COMMENT ON COLUMN merchant_statistic_interval_meta.imeta_serial_id IS 'unique identifier for this type of interval statistic we are tracking'; 319 COMMENT ON COLUMN merchant_statistic_interval_meta.slug IS 'keyword (or name) of the statistic; identifies what the statistic is about; should be a slug suitable for a URI path'; 320 COMMENT ON COLUMN merchant_statistic_interval_meta.description IS 'description of the statistic being tracked'; 321 COMMENT ON COLUMN merchant_statistic_interval_meta.stype IS 'statistic type, what kind of data is being tracked, amount or number'; 322 COMMENT ON COLUMN merchant_statistic_interval_meta.ranges IS 'range of values that is being kept for this statistic, in seconds, must be monotonically increasing'; 323 COMMENT ON COLUMN merchant_statistic_interval_meta.precisions IS 'determines how precisely we track which events fall into the range at the same index (allowing us to coalesce events with timestamps in proximity close to the given precision), in seconds, 0 is not allowed'; 324 325 CREATE TABLE merchant_token_families ( 326 token_family_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 327 slug TEXT NOT NULL UNIQUE, 328 name TEXT NOT NULL, 329 description TEXT, 330 description_i18n JSONB NOT NULL, 331 valid_after INT8 NOT NULL, 332 valid_before INT8 NOT NULL, 333 duration INT8 NOT NULL, 334 kind TEXT NOT NULL, 335 issued INT8 DEFAULT 0, 336 used INT8 DEFAULT 0, 337 validity_granularity INT8 DEFAULT '2592000000000'::INT8 NOT NULL, 338 start_offset INT8 DEFAULT 0 NOT NULL, 339 cipher_choice TEXT DEFAULT 'rsa(2048)'::text NOT NULL, 340 extra_data JSONB, 341 CONSTRAINT merchant_token_families_kind_check 342 CHECK ((kind = ANY (ARRAY['subscription'::text, 'discount'::text]))), 343 CONSTRAINT merchant_token_families_validity_granularity_check 344 CHECK ((validity_granularity = ANY (ARRAY[(60000000)::INT8, 345 '3600000000'::INT8, '86400000000'::INT8, '604800000000'::INT8, 346 '2592000000000'::INT8, '7776000000000'::INT8, 347 '31536000000000'::INT8]))) 348 ); 349 COMMENT ON TABLE merchant_token_families IS 'Token families configured by the merchant.'; 350 COMMENT ON COLUMN merchant_token_families.slug IS 'Unique slug for the token family.'; 351 COMMENT ON COLUMN merchant_token_families.name IS 'Name of the token family.'; 352 COMMENT ON COLUMN merchant_token_families.description IS 'Human-readable description or details about the token family.'; 353 COMMENT ON COLUMN merchant_token_families.description_i18n IS 'JSON map from IETF BCP 47 language tags to localized descriptions'; 354 COMMENT ON COLUMN merchant_token_families.valid_after IS 'Start time of the token family''s validity period.'; 355 COMMENT ON COLUMN merchant_token_families.valid_before IS 'End time of the token family''s validity period.'; 356 COMMENT ON COLUMN merchant_token_families.duration IS 'Duration of the token.'; 357 COMMENT ON COLUMN merchant_token_families.kind IS 'Kind of the token (e.g., subscription, discount).'; 358 COMMENT ON COLUMN merchant_token_families.issued IS 'Counter for the number of tokens issued for this token family.'; 359 COMMENT ON COLUMN merchant_token_families.used IS 'FIXME'; 360 COMMENT ON COLUMN merchant_token_families.validity_granularity IS 'To compute key lifetimes, we first round the payment deadline down to a multiple of this time; supported values are one minute, one hour, a day, a week, 30 days, 90 days or a year (indicatited using 365 days); adding the start_offset gets the start validity time; adding the duration to get the signature_valid_until value for the key'; 361 COMMENT ON COLUMN merchant_token_families.start_offset IS 'This allows shifting the validity period of signatures to start a bit before the time rounded to the precision. For example, Swiss vignettes are valid for 14 months, from December of year X to January of year X+2. This can be achieve by setting a start_offset of 30 days, and a duration of 14 months and a precision of 1 year. The value given is in microseconds (but will be rounded to seconds).'; 362 COMMENT ON COLUMN merchant_token_families.cipher_choice IS 'Specifies the type of cipher that should be used for this token family. Currently supported values are "cs" and "rsa($LEN)" where $LEN is the key length in bits.'; 363 COMMENT ON COLUMN merchant_token_families.extra_data IS 'JSON field with family-specific meta data, such as the trusted_domains for subscriptions or expected_domains for discount tokens'; 364 365 CREATE TABLE merchant_unclaim_signatures ( 366 unclaim_serial INT8 GENERATED BY DEFAULT AS IDENTITY UNIQUE, 367 h_contract_terms BYTEA NOT NULL, 368 unclaim_sig BYTEA NOT NULL PRIMARY KEY, 369 expiration_time INT8 NOT NULL, 370 CONSTRAINT merchant_unclaim_signatures_h_contract_terms_check CHECK ((LENGTH(h_contract_terms) = 64)), 371 CONSTRAINT merchant_unclaim_signatures_unclaim_sig_check CHECK ((LENGTH(unclaim_sig) = 64)) 372 ); 373 COMMENT ON TABLE merchant_unclaim_signatures IS 'Here we store proofs of wallets unclaiming a claim to a contract. Right now not really used, but theoretically legally important as this shows that the wallet was the one abandoing its rights to the proposal.'; 374 COMMENT ON COLUMN merchant_unclaim_signatures.h_contract_terms IS 'Hash over the contract terms of the unclaimed contract'; 375 COMMENT ON COLUMN merchant_unclaim_signatures.unclaim_sig IS 'Signature of purpose CONTRACT_UNCLAIM'; 376 COMMENT ON COLUMN merchant_unclaim_signatures.expiration_time IS 'Payment deadline of the original contract, at this point we should be OK to delete the unclaim signature via garbage collection.'; 377 CREATE INDEX merchant_unclaim_signatures_by_expiration 378 ON merchant_unclaim_signatures (expiration_time); 379 COMMENT ON INDEX merchant_unclaim_signatures_by_expiration IS 'For garbage collection of unclaim signatrues after payment deadlines have been passed.'; 380 381 CREATE TABLE merchant_webhook ( 382 webhook_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 383 webhook_id TEXT NOT NULL UNIQUE, 384 event_type TEXT NOT NULL, 385 url TEXT NOT NULL, 386 http_method TEXT NOT NULL, 387 header_template TEXT, 388 body_template TEXT 389 ); 390 COMMENT ON TABLE merchant_webhook IS 'webhook used by the merchant (may be incomplete, frontend can override)'; 391 COMMENT ON COLUMN merchant_webhook.webhook_id IS 'slug identifying the webhook in protocols'; 392 COMMENT ON COLUMN merchant_webhook.event_type IS 'Event of the webhook'; 393 COMMENT ON COLUMN merchant_webhook.url IS 'URL to make the request to'; 394 COMMENT ON COLUMN merchant_webhook.http_method IS 'http method use by the merchant'; 395 COMMENT ON COLUMN merchant_webhook.header_template IS 'Template for the header of the webhook, to be modified based on trigger data'; 396 COMMENT ON COLUMN merchant_webhook.body_template IS 'Template for the body of the webhook, to be modified based on trigger data'; 397 398 CREATE TABLE tan_challenges ( 399 challenge_id INT8 GENERATED BY DEFAULT AS IDENTITY UNIQUE, 400 h_body BYTEA NOT NULL, 401 salt BYTEA NOT NULL, 402 op merchant.op_enum NOT NULL, 403 code TEXT NOT NULL, 404 creation_date INT8 NOT NULL, 405 expiration_date INT8 NOT NULL, 406 retransmission_date INT8 DEFAULT 0 NOT NULL, 407 confirmation_date INT8, 408 retry_counter INT4 NOT NULL, 409 tan_channel merchant.tan_enum NOT NULL, 410 required_address TEXT NOT NULL, 411 CONSTRAINT tan_challenges_h_body_check CHECK ((LENGTH(h_body) = 32)), 412 CONSTRAINT tan_challenges_salt_check CHECK ((LENGTH(salt) = 16)) 413 ); 414 COMMENT ON TABLE tan_challenges IS 'Stores multi-factor authorization (MFA) challenges'; 415 COMMENT ON COLUMN tan_challenges.challenge_id IS 'Unique identifier for the challenge'; 416 COMMENT ON COLUMN tan_challenges.h_body IS 'Salted hash of the body of the original request that triggered the challenge, to be replayed once the challenge is satisfied.'; 417 COMMENT ON COLUMN tan_challenges.salt IS 'Salt used when hashing the original body.'; 418 COMMENT ON COLUMN tan_challenges.op IS 'The protected operation to run after the challenge'; 419 COMMENT ON COLUMN tan_challenges.code IS 'The pin code sent to the user and verified'; 420 COMMENT ON COLUMN tan_challenges.creation_date IS 'Creation date of the code'; 421 COMMENT ON COLUMN tan_challenges.expiration_date IS 'When will the code expire'; 422 COMMENT ON COLUMN tan_challenges.retransmission_date IS 'When did we last transmit the challenge to the user'; 423 COMMENT ON COLUMN tan_challenges.confirmation_date IS 'When was this challenge successfully verified, NULL if pending'; 424 COMMENT ON COLUMN tan_challenges.retry_counter IS 'How many tries are left for this code; must be > 0'; 425 COMMENT ON COLUMN tan_challenges.tan_channel IS 'TAN channel to use, if NULL use customer configured one'; 426 COMMENT ON COLUMN tan_challenges.required_address IS 'Address to which the challenge will be sent'; 427 CREATE INDEX tan_challenges_expiration_index 428 ON tan_challenges (expiration_date); 429 COMMENT ON INDEX tan_challenges_expiration_index IS 'for garbage collection'; 430 431 CREATE TABLE merchant_builtin_unit_overrides ( 432 builtin_unit_serial INT8 NOT NULL PRIMARY KEY, 433 override_allow_fraction BOOLEAN, 434 override_precision_level INT4, 435 override_active BOOLEAN, 436 CONSTRAINT merchant_builtin_unit_overrides_override_precision_level_check 437 CHECK (((override_precision_level >= 0) AND (override_precision_level <= 6))), 438 CONSTRAINT merchant_builtin_unit_overrides_builtin_unit_serial_fkey 439 FOREIGN KEY (builtin_unit_serial) 440 REFERENCES merchant.merchant_builtin_units(unit_serial) ON DELETE CASCADE 441 ); 442 COMMENT ON TABLE merchant_builtin_unit_overrides IS 'Per-instance overrides for builtin units (fraction policy and visibility).'; 443 COMMENT ON COLUMN merchant_builtin_unit_overrides.override_allow_fraction IS 'FIXME'; 444 COMMENT ON COLUMN merchant_builtin_unit_overrides.override_precision_level IS 'FIXME'; 445 COMMENT ON COLUMN merchant_builtin_unit_overrides.override_active IS 'FIXME'; 446 447 -- =================================================================== 448 -- Tier 1: tables referencing only Tier 0 (and global merchant.*) tables. 449 -- =================================================================== 450 451 CREATE TABLE merchant_deposit_confirmations ( 452 deposit_confirmation_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 453 order_serial INT8, 454 deposit_timestamp INT8 NOT NULL, 455 exchange_url TEXT NOT NULL, 456 total_without_fee merchant.taler_amount_currency NOT NULL, 457 wire_fee merchant.taler_amount_currency NOT NULL, 458 signkey_serial INT8 NOT NULL, 459 exchange_sig BYTEA NOT NULL, 460 account_serial INT8 NOT NULL, 461 wire_transfer_deadline INT8 DEFAULT 0 NOT NULL, 462 wire_pending BOOLEAN DEFAULT TRUE NOT NULL, 463 exchange_failure TEXT, 464 retry_backoff INT8 DEFAULT 0 NOT NULL, 465 CONSTRAINT merchant_deposit_confirmations_exchange_sig_check CHECK ((LENGTH(exchange_sig) = 64)), 466 UNIQUE (order_serial, exchange_sig), 467 CONSTRAINT merchant_deposit_confirmations_account_serial_fkey 468 FOREIGN KEY (account_serial) 469 REFERENCES merchant_accounts(account_serial) ON DELETE CASCADE, 470 CONSTRAINT merchant_deposit_confirmations_order_serial_fkey 471 FOREIGN KEY (order_serial) 472 REFERENCES merchant_contract_terms(order_serial) ON DELETE CASCADE, 473 CONSTRAINT merchant_deposit_confirmations_signkey_serial_fkey 474 FOREIGN KEY (signkey_serial) 475 REFERENCES merchant.merchant_exchange_signing_keys(signkey_serial) ON DELETE CASCADE 476 ); 477 COMMENT ON TABLE merchant_deposit_confirmations IS 'Table with the deposit confirmations for each coin we deposited at the exchange'; 478 COMMENT ON COLUMN merchant_deposit_confirmations.order_serial IS 'Identifies the entry in the contract_terms table that was paid by this deposit'; 479 COMMENT ON COLUMN merchant_deposit_confirmations.deposit_timestamp IS 'Time when the exchange generated the deposit confirmation'; 480 COMMENT ON COLUMN merchant_deposit_confirmations.exchange_url IS 'Which exchange received the deposit; a single contract may be paid by multiple (batch) deposits to multiple exchanges'; 481 COMMENT ON COLUMN merchant_deposit_confirmations.total_without_fee IS 'Total amount that was (batch) deposited, excluding deposit fees (irrespective of who had to pay the fees); so this is the amount the merchant will be credited for this deposit (minus applicable wire fees after aggregation)'; 482 COMMENT ON COLUMN merchant_deposit_confirmations.wire_fee IS 'We MAY want to see if we should try to get this via merchant_exchange_wire_fees (not sure, may be too complicated with the date range, etc.)'; 483 COMMENT ON COLUMN merchant_deposit_confirmations.signkey_serial IS 'Online signing key of the exchange on the deposit confirmation'; 484 COMMENT ON COLUMN merchant_deposit_confirmations.exchange_sig IS 'Signature of the exchange over the deposit confirmation'; 485 COMMENT ON COLUMN merchant_deposit_confirmations.account_serial IS 'Identifies the bank account of the merchant that will receive the payment'; 486 COMMENT ON COLUMN merchant_deposit_confirmations.wire_transfer_deadline IS 'when should the exchange make the wire transfer at the latest'; 487 COMMENT ON COLUMN merchant_deposit_confirmations.wire_pending IS 'true if we are awaiting wire details for a deposit of this purchase (and are not blocked on KYC); false once the exchange says that the wire transfer has happened (does not mean that we confirmed it happened though)'; 488 COMMENT ON COLUMN merchant_deposit_confirmations.exchange_failure IS 'Text describing exchange failures in making timely wire transfers for this deposit confirmation'; 489 COMMENT ON COLUMN merchant_deposit_confirmations.retry_backoff IS 'exponentially increasing value we add to the wire_transfer_deadline on each failure to confirm the wire transfer'; 490 CREATE INDEX merchant_deposit_confirmations_by_pending_wire 491 ON merchant_deposit_confirmations (exchange_url, wire_transfer_deadline) 492 WHERE wire_pending; 493 494 CREATE TABLE merchant_expected_transfers ( 495 expected_credit_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 496 exchange_url TEXT NOT NULL, 497 wtid BYTEA NOT NULL, 498 expected_credit_amount merchant.taler_amount_currency, 499 wire_fee merchant.taler_amount_currency, 500 account_serial INT8 NOT NULL, 501 expected_time INT8 NOT NULL, 502 retry_time INT8 DEFAULT 0 NOT NULL, 503 last_http_status INT4, 504 last_ec INT4, 505 last_detail TEXT, 506 retry_needed BOOLEAN DEFAULT TRUE NOT NULL, 507 signkey_serial INT8, 508 exchange_sig BYTEA, 509 h_details BYTEA, 510 confirmed BOOLEAN DEFAULT FALSE NOT NULL, 511 CONSTRAINT merchant_expected_transfers_exchange_sig_check CHECK ((LENGTH(exchange_sig) = 64)), 512 CONSTRAINT merchant_expected_transfers_h_details_check CHECK ((LENGTH(h_details) = 64)), 513 CONSTRAINT merchant_expected_transfers_wtid_check CHECK ((LENGTH(wtid) = 32)), 514 UNIQUE (wtid, exchange_url, account_serial), 515 CONSTRAINT merchant_expected_transfers_account_serial_fkey 516 FOREIGN KEY (account_serial) 517 REFERENCES merchant_accounts(account_serial) ON DELETE CASCADE, 518 CONSTRAINT merchant_expected_transfers_signkey_serial_fkey 519 FOREIGN KEY (signkey_serial) 520 REFERENCES merchant.merchant_exchange_signing_keys(signkey_serial) ON DELETE CASCADE 521 ); 522 COMMENT ON TABLE merchant_expected_transfers IS 'expected incoming wire transfers'; 523 COMMENT ON COLUMN merchant_expected_transfers.expected_credit_serial IS 'Unique identifier for this expected wire transfer in this backend'; 524 COMMENT ON COLUMN merchant_expected_transfers.exchange_url IS 'Base URL of the exchange that originated the wire transfer as extracted from the wire transfer subject'; 525 COMMENT ON COLUMN merchant_expected_transfers.wtid IS 'Unique wire transfer identifier (or at least, should be unique by protocol) as selected by the exchange and extracted from the wire transfer subject'; 526 COMMENT ON COLUMN merchant_expected_transfers.expected_credit_amount IS 'expected actual value of the (aggregated) wire transfer, excluding the wire fee; NULL if unknown'; 527 COMMENT ON COLUMN merchant_expected_transfers.wire_fee IS 'wire fee the exchange claims to have charged us; NULL if unknown'; 528 COMMENT ON COLUMN merchant_expected_transfers.account_serial IS 'Merchant bank account that should receive this wire transfer; also implies the merchant instance implicated by the wire transfer'; 529 COMMENT ON COLUMN merchant_expected_transfers.expected_time IS 'Time when we should expect the exchange do do the wire transfer'; 530 COMMENT ON COLUMN merchant_expected_transfers.retry_time IS 'Time when we should next inquire at the exchange about this wire transfer; used by taler-merchant-reconciliation to limit retries with the exchange in case of failures'; 531 COMMENT ON COLUMN merchant_expected_transfers.last_http_status IS 'HTTP status of the last request to the exchange, 0 on timeout or if there was no request (200 on success)'; 532 COMMENT ON COLUMN merchant_expected_transfers.last_ec IS 'Taler error code from the last request to the exchange, 0 on success or if there was no request'; 533 COMMENT ON COLUMN merchant_expected_transfers.last_detail IS 'Taler error detail from the last request to the exchange, NULL on success or if there was no request'; 534 COMMENT ON COLUMN merchant_expected_transfers.retry_needed IS 'true if we need to retry the HTTP request to the exchange (never did it, or transient failure)'; 535 COMMENT ON COLUMN merchant_expected_transfers.signkey_serial IS 'Identifies the online signing key of the exchange used to make the exchange_sig'; 536 COMMENT ON COLUMN merchant_expected_transfers.exchange_sig IS 'Signature over the aggregation response from the exchange, or NULL on error or if we did not yet make that request'; 537 COMMENT ON COLUMN merchant_expected_transfers.h_details IS 'Hash over the aggregation details returned by the exchange, provided here for fast exchange_sig validation'; 538 COMMENT ON COLUMN merchant_expected_transfers.confirmed IS 'true once the merchant confirmed that this transfer was received and a matching transfer exists in the merchant_transfers table; set automatically via INSERT TRIGGER merchant_expected_transfers_insert_trigger'; 539 CREATE INDEX merchant_expected_transfers_by_open 540 ON merchant_expected_transfers (retry_time) 541 WHERE ((NOT confirmed) OR retry_needed); 542 COMMENT ON INDEX merchant_expected_transfers_by_open IS 'For select_open_transfers'; 543 544 CREATE TABLE merchant_inventory ( 545 product_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 546 product_id TEXT NOT NULL UNIQUE, 547 description TEXT NOT NULL, 548 description_i18n JSONB NOT NULL, 549 unit TEXT NOT NULL, 550 image TEXT NOT NULL, 551 taxes JSONB NOT NULL, 552 total_stock INT8 NOT NULL, 553 total_sold INT8 DEFAULT 0 NOT NULL, 554 total_lost INT8 DEFAULT 0 NOT NULL, 555 address JSONB NOT NULL, 556 next_restock INT8 NOT NULL, 557 minimum_age INT4 DEFAULT 0 NOT NULL, 558 product_name TEXT NOT NULL, 559 image_hash TEXT, 560 price_array merchant.taler_amount_currency[] 561 DEFAULT ARRAY[]::merchant.taler_amount_currency[] NOT NULL, 562 total_stock_frac INT4 DEFAULT 0 NOT NULL, 563 total_sold_frac INT4 DEFAULT 0 NOT NULL, 564 total_lost_frac INT4 DEFAULT 0 NOT NULL, 565 allow_fractional_quantity BOOLEAN DEFAULT FALSE NOT NULL, 566 fractional_precision_level INT4 DEFAULT 0 NOT NULL, 567 product_group_serial INT8, 568 money_pot_serial INT8, 569 price_is_net BOOLEAN DEFAULT FALSE, 570 CONSTRAINT merchant_inventory_money_pot_serial_fkey 571 FOREIGN KEY (money_pot_serial) 572 REFERENCES merchant_money_pots(money_pot_serial) ON DELETE SET NULL, 573 CONSTRAINT merchant_inventory_product_group_serial_fkey 574 FOREIGN KEY (product_group_serial) 575 REFERENCES merchant_product_groups(product_group_serial) ON DELETE SET NULL 576 ); 577 COMMENT ON TABLE merchant_inventory IS 'products offered by the merchant (may be incomplete, frontend can override)'; 578 COMMENT ON COLUMN merchant_inventory.product_id IS 'slug identifying the product in protocols'; 579 COMMENT ON COLUMN merchant_inventory.description IS 'Human-readable product description'; 580 COMMENT ON COLUMN merchant_inventory.description_i18n IS 'JSON map from IETF BCP 47 language tags to localized descriptions'; 581 COMMENT ON COLUMN merchant_inventory.unit IS 'Unit of sale for the product (liters, kilograms, packages)'; 582 COMMENT ON COLUMN merchant_inventory.image IS 'NOT NULL, but can be 0 bytes; must contain an ImageDataUrl'; 583 COMMENT ON COLUMN merchant_inventory.taxes IS 'JSON array containing taxes the merchant pays, must be JSON, but can be just "[]"'; 584 COMMENT ON COLUMN merchant_inventory.total_stock IS 'A value of -1 is used for unlimited (electronic good), may never be lowered'; 585 COMMENT ON COLUMN merchant_inventory.total_sold IS 'Number of products sold, must be below total_stock, non-negative, may never be lowered'; 586 COMMENT ON COLUMN merchant_inventory.total_lost IS 'Number of products that used to be in stock but were lost (spoiled, damaged), may never be lowered; total_stock >= total_sold + total_lost must always hold'; 587 COMMENT ON COLUMN merchant_inventory.address IS 'JSON formatted Location of where the product is stocked'; 588 COMMENT ON COLUMN merchant_inventory.next_restock IS 'GNUnet absolute time i ndicating when the next restock is expected. 0 for unknown.'; 589 COMMENT ON COLUMN merchant_inventory.minimum_age IS 'Minimum age of the customer in years, to be used if an exchange supports the age restriction extension.'; 590 COMMENT ON COLUMN merchant_inventory.product_name IS 'Name of the product'; 591 COMMENT ON COLUMN merchant_inventory.image_hash IS 'SHA-256 hash of the base64-encoded image data, used by wallets to fetch product images.'; 592 COMMENT ON COLUMN merchant_inventory.price_array IS 'List of unit prices available for the product (multiple tiers supported).'; 593 COMMENT ON COLUMN merchant_inventory.total_stock_frac IS 'Fractional part of stock in units of 1/1000000 of the base value'; 594 COMMENT ON COLUMN merchant_inventory.total_sold_frac IS 'Fractional part of units sold in units of 1/1000000 of the base value'; 595 COMMENT ON COLUMN merchant_inventory.total_lost_frac IS 'Fractional part of units lost in units of 1/1000000 of the base value'; 596 COMMENT ON COLUMN merchant_inventory.allow_fractional_quantity IS 'Whether fractional stock (total_stock_frac) should be honored for this product'; 597 COMMENT ON COLUMN merchant_inventory.fractional_precision_level IS 'Preset number of decimal places for fractional quantities'; 598 COMMENT ON COLUMN merchant_inventory.product_group_serial IS 'Specifies the product group a given product is a member of. If NULL, the product is in the __default__ group'; 599 COMMENT ON COLUMN merchant_inventory.money_pot_serial IS 'Specifies that sales amounts of this product (excluding taxes on the product that have their own pot specified) should by default be added to the given money pot. If NULL, the money pot rules of the overall order apply instead.'; 600 COMMENT ON COLUMN merchant_inventory.price_is_net IS 'If true, the price given is the net price; if false, it is the gross price.'; 601 CREATE INDEX merchant_inventory_by_image_hash 602 ON merchant_inventory (image_hash); 603 CREATE INDEX trgm_idx_products_by_description 604 ON merchant_inventory USING gin (lower(description) public.gin_trgm_ops); 605 CREATE INDEX trgm_idx_products_by_name 606 ON merchant_inventory USING gin (lower(product_name) public.gin_trgm_ops); 607 608 CREATE TABLE merchant_kyc ( 609 kyc_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY UNIQUE, 610 kyc_timestamp INT8 NOT NULL, 611 kyc_ok BOOLEAN DEFAULT FALSE NOT NULL, 612 account_serial INT8 NOT NULL, 613 exchange_url TEXT NOT NULL, 614 access_token BYTEA, 615 exchange_http_status INT4 DEFAULT 0, 616 exchange_ec_code INT4 DEFAULT 0, 617 aml_review BOOLEAN DEFAULT FALSE, 618 jaccount_limits JSONB, 619 last_rule_gen INT8 DEFAULT 0 NOT NULL, 620 next_kyc_poll INT8 DEFAULT 0 NOT NULL, 621 kyc_backoff INT8 DEFAULT 0 NOT NULL, 622 CONSTRAINT access_token_LENGTH_check CHECK ((LENGTH(access_token) = 32)), 623 PRIMARY KEY (account_serial, exchange_url), 624 CONSTRAINT merchant_kyc_account_serial_fkey 625 FOREIGN KEY (account_serial) 626 REFERENCES merchant_accounts(account_serial) ON DELETE CASCADE 627 ); 628 COMMENT ON TABLE merchant_kyc IS 'Status of the KYC process of a merchant account at an exchange'; 629 COMMENT ON COLUMN merchant_kyc.kyc_timestamp IS 'Last time we checked our KYC status at the exchange. Useful to re-check if the status is very stale. Also the timestamp used for the exchange signature (if present).'; 630 COMMENT ON COLUMN merchant_kyc.kyc_ok IS 'true if the KYC check was passed successfully'; 631 COMMENT ON COLUMN merchant_kyc.account_serial IS 'Which bank account of the merchant is the KYC status for'; 632 COMMENT ON COLUMN merchant_kyc.exchange_url IS 'Which exchange base URL is this KYC status valid for'; 633 COMMENT ON COLUMN merchant_kyc.access_token IS 'Access token required to begin the KYC process'; 634 COMMENT ON COLUMN merchant_kyc.exchange_http_status IS 'Last HTTP status returned by the exchange when inquiring about our KYC status.'; 635 COMMENT ON COLUMN merchant_kyc.exchange_ec_code IS 'Last Taler error code returned by the exchange when inquiring about our KYC status.'; 636 COMMENT ON COLUMN merchant_kyc.aml_review IS 'True if our account is under AML review according to the exchange.'; 637 COMMENT ON COLUMN merchant_kyc.jaccount_limits IS 'JSON with AccountLimits that apply to this account'; 638 COMMENT ON COLUMN merchant_kyc.last_rule_gen IS 'Row ID from the exchange identifying the last decision change to KYC rules on this account. To be used when long-polling to ask for changes. 0 if no custom decisions were taken for the account'; 639 COMMENT ON COLUMN merchant_kyc.next_kyc_poll IS 'When should we next do a KYC poll on this exchange and bank account'; 640 COMMENT ON COLUMN merchant_kyc.kyc_backoff IS 'What is the current backoff value between KYC polls'; 641 CREATE INDEX merchant_kyc_by_next_kyc_poll 642 ON merchant_kyc (next_kyc_poll); 643 644 CREATE TABLE merchant_order_token_blinded_sigs ( 645 order_token_bs_serial INT8 GENERATED BY DEFAULT AS IDENTITY, 646 order_serial INT8 NOT NULL, 647 token_index INT4 NOT NULL, 648 token_blinded_signature BYTEA NOT NULL, 649 token_hash BYTEA NOT NULL, 650 CONSTRAINT merchant_order_token_blinded_sigs_token_hash_check CHECK ((LENGTH(token_hash) = 64)), 651 PRIMARY KEY (order_serial, token_index), 652 CONSTRAINT merchant_order_token_blinded_sigs_order_serial_fkey 653 FOREIGN KEY (order_serial) 654 REFERENCES merchant_contract_terms(order_serial) ON DELETE CASCADE 655 ); 656 COMMENT ON TABLE merchant_order_token_blinded_sigs IS 'Table linking merchant orders with Donau BUDIS information'; 657 COMMENT ON COLUMN merchant_order_token_blinded_sigs.order_token_bs_serial IS 'Unique serial identifier for token order linkage'; 658 COMMENT ON COLUMN merchant_order_token_blinded_sigs.order_serial IS 'Foreign key linking to the corresponding merchant order'; 659 COMMENT ON COLUMN merchant_order_token_blinded_sigs.token_index IS 'offset of the given signature in the output token array'; 660 COMMENT ON COLUMN merchant_order_token_blinded_sigs.token_blinded_signature IS 'Blinded signature of the token associated with the order'; 661 COMMENT ON COLUMN merchant_order_token_blinded_sigs.token_hash IS 'Hash of the token'; 662 663 CREATE TABLE merchant_pending_webhooks ( 664 webhook_pending_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 665 webhook_serial INT8 NOT NULL, 666 next_attempt INT8 DEFAULT 0 NOT NULL, 667 retries INT4 DEFAULT 0 NOT NULL, 668 url TEXT NOT NULL, 669 http_method TEXT NOT NULL, 670 header TEXT, 671 body TEXT, 672 CONSTRAINT merchant_pending_webhooks_webhook_serial_fkey 673 FOREIGN KEY (webhook_serial) 674 REFERENCES merchant_webhook(webhook_serial) ON DELETE CASCADE 675 ); 676 COMMENT ON TABLE merchant_pending_webhooks IS 'webhooks that still need to be executed by the merchant'; 677 COMMENT ON COLUMN merchant_pending_webhooks.webhook_serial IS 'Reference to the configured webhook template'; 678 COMMENT ON COLUMN merchant_pending_webhooks.next_attempt IS 'Time when we should make the next request to the webhook'; 679 COMMENT ON COLUMN merchant_pending_webhooks.retries IS 'How often have we tried this request so far'; 680 COMMENT ON COLUMN merchant_pending_webhooks.url IS 'URL to make the request to'; 681 COMMENT ON COLUMN merchant_pending_webhooks.http_method IS 'http method use for the webhook'; 682 COMMENT ON COLUMN merchant_pending_webhooks.header IS 'Header of the webhook'; 683 COMMENT ON COLUMN merchant_pending_webhooks.body IS 'Body of the webhook'; 684 685 CREATE TABLE merchant_refunds ( 686 refund_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 687 order_serial INT8 NOT NULL, 688 rtransaction_id INT8 NOT NULL, 689 refund_timestamp INT8 NOT NULL, 690 coin_pub BYTEA NOT NULL, 691 reason TEXT NOT NULL, 692 refund_amount merchant.taler_amount_currency NOT NULL, 693 UNIQUE (order_serial, coin_pub, rtransaction_id), 694 CONSTRAINT merchant_refunds_order_serial_fkey 695 FOREIGN KEY (order_serial) 696 REFERENCES merchant_contract_terms(order_serial) ON DELETE CASCADE 697 ); 698 COMMENT ON TABLE merchant_refunds IS 'Refunds approved by the merchant (backoffice) logic, excludes abort refunds'; 699 COMMENT ON COLUMN merchant_refunds.order_serial IS 'order for which the refund is being applied'; 700 COMMENT ON COLUMN merchant_refunds.rtransaction_id IS 'Needed for uniqueness in case a refund is increased for the same order'; 701 COMMENT ON COLUMN merchant_refunds.refund_timestamp IS 'Needed for grouping of refunds in the wallet UI; has no semantics in the protocol (only for UX), but should be from the time when the merchant internally approved the refund'; 702 COMMENT ON COLUMN merchant_refunds.coin_pub IS 'public key of the coin that is going to be refunded'; 703 COMMENT ON COLUMN merchant_refunds.reason IS 'human-readable reason given for the refund'; 704 COMMENT ON COLUMN merchant_refunds.refund_amount IS 'refund amount granted on this coin'; 705 CREATE INDEX merchant_refunds_by_coin_and_order 706 ON merchant_refunds (coin_pub, order_serial); 707 708 CREATE TABLE merchant_statistic_amount_event ( 709 aevent_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 710 imeta_serial_id INT8, 711 slot INT8 NOT NULL, 712 delta_curr character varying(12) NOT NULL, 713 delta_value INT8 NOT NULL, 714 delta_frac INT4 NOT NULL, 715 CONSTRAINT event_key UNIQUE (imeta_serial_id, delta_curr, slot), 716 CONSTRAINT merchant_statistic_amount_event_imeta_serial_id_fkey 717 FOREIGN KEY (imeta_serial_id) 718 REFERENCES merchant_statistic_interval_meta(imeta_serial_id) ON DELETE CASCADE 719 ); 720 COMMENT ON TABLE merchant_statistic_amount_event IS 'amount to decrement an interval statistic by when a certain time value is reached'; 721 COMMENT ON COLUMN merchant_statistic_amount_event.aevent_serial_id IS 'unique identifier for this amount event'; 722 COMMENT ON COLUMN merchant_statistic_amount_event.imeta_serial_id IS 'identifies what the statistic is about; must be of clazz interval and of stype amount'; 723 COMMENT ON COLUMN merchant_statistic_amount_event.slot IS 'identifies the time slot at which the given event(s) happened'; 724 COMMENT ON COLUMN merchant_statistic_amount_event.delta_curr IS 'currency of the total cumulative amount that was added at the time identified by slot'; 725 COMMENT ON COLUMN merchant_statistic_amount_event.delta_value IS 'total cumulative amount (value) that was added at the time identified by slot'; 726 COMMENT ON COLUMN merchant_statistic_amount_event.delta_frac IS 'total cumulative amount (fraction) that was added at the time identified by slot'; 727 728 CREATE TABLE merchant_statistic_bucket_amount ( 729 bmeta_serial_id INT8 NOT NULL, 730 bucket_start INT8 NOT NULL, 731 bucket_range merchant.statistic_range NOT NULL, 732 curr character varying(12) NOT NULL, 733 cumulative_value INT8 NOT NULL, 734 cumulative_frac INT4 NOT NULL, 735 PRIMARY KEY (bmeta_serial_id, curr, bucket_start, bucket_range), 736 CONSTRAINT merchant_statistic_bucket_amount_bmeta_serial_id_fkey 737 FOREIGN KEY (bmeta_serial_id) 738 REFERENCES merchant_statistic_bucket_meta(bmeta_serial_id) ON DELETE CASCADE 739 ); 740 COMMENT ON TABLE merchant_statistic_bucket_amount IS 'various amount statistics (in various currencies) being tracked'; 741 COMMENT ON COLUMN merchant_statistic_bucket_amount.bmeta_serial_id IS 'identifies what the statistic is about'; 742 COMMENT ON COLUMN merchant_statistic_bucket_amount.bucket_start IS 'start date for the bucket in seconds since the epoch'; 743 COMMENT ON COLUMN merchant_statistic_bucket_amount.bucket_range IS 'range of the bucket'; 744 COMMENT ON COLUMN merchant_statistic_bucket_amount.curr IS 'currency which this statistic is tracking the amount for'; 745 COMMENT ON COLUMN merchant_statistic_bucket_amount.cumulative_value IS 'amount in the respective currency, non-fractional amount value'; 746 COMMENT ON COLUMN merchant_statistic_bucket_amount.cumulative_frac IS 'amount in the respective currency, fraction in units of 1/100000000 of the base value'; 747 748 CREATE TABLE merchant_statistic_bucket_counter ( 749 bmeta_serial_id INT8 NOT NULL, 750 bucket_start INT8 NOT NULL, 751 bucket_range merchant.statistic_range NOT NULL, 752 cumulative_number INT8 NOT NULL, 753 PRIMARY KEY (bmeta_serial_id, bucket_start, bucket_range), 754 CONSTRAINT merchant_statistic_bucket_counter_bmeta_serial_id_fkey 755 FOREIGN KEY (bmeta_serial_id) 756 REFERENCES merchant_statistic_bucket_meta(bmeta_serial_id) ON DELETE CASCADE 757 ); 758 COMMENT ON TABLE merchant_statistic_bucket_counter IS 'various numeric statistics (cumulative counters) being tracked by bucket into which they fall'; 759 COMMENT ON COLUMN merchant_statistic_bucket_counter.bmeta_serial_id IS 'identifies what the statistic is about'; 760 COMMENT ON COLUMN merchant_statistic_bucket_counter.bucket_start IS 'start date for the bucket in seconds since the epoch'; 761 COMMENT ON COLUMN merchant_statistic_bucket_counter.bucket_range IS 'range of the bucket'; 762 COMMENT ON COLUMN merchant_statistic_bucket_counter.cumulative_number IS 'aggregate (sum) of tracked by the statistic; what exactly is tracked is determined by the keyword'; 763 764 CREATE TABLE merchant_statistic_counter_event ( 765 nevent_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 766 imeta_serial_id INT8, 767 slot INT8 NOT NULL, 768 delta INT8 NOT NULL, 769 UNIQUE (imeta_serial_id, slot), 770 CONSTRAINT merchant_statistic_counter_event_imeta_serial_id_fkey 771 FOREIGN KEY (imeta_serial_id) 772 REFERENCES merchant_statistic_interval_meta(imeta_serial_id) ON DELETE CASCADE 773 ); 774 COMMENT ON TABLE merchant_statistic_counter_event IS 'number to decrement an interval statistic by when a certain time value is reached'; 775 COMMENT ON COLUMN merchant_statistic_counter_event.nevent_serial_id IS 'unique identifier for this number event'; 776 COMMENT ON COLUMN merchant_statistic_counter_event.imeta_serial_id IS 'identifies what the statistic is about; must be of stype number'; 777 COMMENT ON COLUMN merchant_statistic_counter_event.slot IS 'identifies the time slot at which the given event(s) happened, rounded down by the respective precisions value'; 778 COMMENT ON COLUMN merchant_statistic_counter_event.delta IS 'total cumulative number that was added at the time identified by slot'; 779 780 CREATE TABLE merchant_template ( 781 template_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 782 template_id TEXT NOT NULL UNIQUE, 783 template_description TEXT NOT NULL, 784 otp_device_id INT8, 785 template_contract JSONB NOT NULL, 786 editable_defaults JSONB, 787 CONSTRAINT merchant_template_otp_device_id_fkey 788 FOREIGN KEY (otp_device_id) 789 REFERENCES merchant_otp_devices(otp_serial) ON DELETE SET NULL 790 ); 791 COMMENT ON TABLE merchant_template IS 'template used by the merchant (may be incomplete, frontend can override)'; 792 COMMENT ON COLUMN merchant_template.template_id IS 'slug identifying the templates in protocols (and on the QR code)'; 793 COMMENT ON COLUMN merchant_template.template_description IS 'Human-readable template description'; 794 COMMENT ON COLUMN merchant_template.otp_device_id IS 'OTP device ID used to verify payments by the shop using this QR code'; 795 COMMENT ON COLUMN merchant_template.template_contract IS 'The template contract will contains some additional information.'; 796 COMMENT ON COLUMN merchant_template.editable_defaults IS 'JSON object with fields matching the template contract, just with default values that are editable by the user'; 797 798 CREATE TABLE merchant_token_family_keys ( 799 token_family_key_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 800 token_family_serial INT8, 801 pub BYTEA NOT NULL, 802 h_pub BYTEA NOT NULL UNIQUE, 803 priv BYTEA, 804 cipher TEXT NOT NULL, 805 signature_validity_start INT8 DEFAULT 0 NOT NULL, 806 signature_validity_end INT8 DEFAULT 0 NOT NULL, 807 private_key_deleted_at INT8 DEFAULT 0 NOT NULL, 808 private_key_created_at INT8 DEFAULT 0 NOT NULL, 809 CONSTRAINT h_pub_LENGTH_check CHECK ((LENGTH(h_pub) = 64)), 810 CONSTRAINT merchant_token_family_keys_cipher_check CHECK ((cipher = ANY (ARRAY['rsa'::text, 'cs'::text]))), 811 CONSTRAINT merchant_token_family_keys_token_family_serial_fkey 812 FOREIGN KEY (token_family_serial) 813 REFERENCES merchant_token_families(token_family_serial) ON DELETE CASCADE 814 ); 815 COMMENT ON TABLE merchant_token_family_keys IS 'Keys for token families.'; 816 COMMENT ON COLUMN merchant_token_family_keys.token_family_serial IS 'Token family to which the key belongs.'; 817 COMMENT ON COLUMN merchant_token_family_keys.pub IS 'Public key of the token family.'; 818 COMMENT ON COLUMN merchant_token_family_keys.h_pub IS 'Hash of the public key for quick lookup.'; 819 COMMENT ON COLUMN merchant_token_family_keys.priv IS 'Private key of the token family; can be NULL if no more tokens of this family should be issued.'; 820 COMMENT ON COLUMN merchant_token_family_keys.cipher IS 'Cipher used (rsa or cs).'; 821 COMMENT ON COLUMN merchant_token_family_keys.signature_validity_start IS 'Specifies the earliest time at which tokens signed with this key can be considered valid. Allows tokens to be issued way in advance of their validity.'; 822 COMMENT ON COLUMN merchant_token_family_keys.signature_validity_end IS 'Specifies when the tokens signed by this key expire.'; 823 COMMENT ON COLUMN merchant_token_family_keys.private_key_deleted_at IS 'Specifies how long tokens signed by this key can be created, that is the point at which the private key may be deleted. Computed by determining when the *next* validity period starts, or when the overall token family validity period ends.'; 824 COMMENT ON COLUMN merchant_token_family_keys.private_key_created_at IS 'Specifies when the private key was created. Not terribly useful, mostly for debugging.'; 825 826 CREATE TABLE merchant_transfers ( 827 credit_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 828 exchange_url TEXT NOT NULL, 829 wtid BYTEA, 830 credit_amount merchant.taler_amount_currency NOT NULL, 831 account_serial INT8 NOT NULL, 832 bank_serial_id INT8, 833 expected BOOLEAN DEFAULT FALSE, 834 execution_time INT8 DEFAULT 0, 835 CONSTRAINT merchant_transfers_wtid_check CHECK ((LENGTH(wtid) = 32)), 836 CONSTRAINT merchant_transfers_unique UNIQUE (wtid, exchange_url, account_serial, bank_serial_id), 837 CONSTRAINT merchant_transfers_account_serial_fkey 838 FOREIGN KEY (account_serial) 839 REFERENCES merchant_accounts(account_serial) ON DELETE CASCADE 840 ); 841 COMMENT ON TABLE merchant_transfers IS 'table represents confirmed incoming wire transfers'; 842 COMMENT ON COLUMN merchant_transfers.credit_serial IS 'Unique identifier for this wire transfer in this backend'; 843 COMMENT ON COLUMN merchant_transfers.exchange_url IS 'Base URL of the exchange that originated the wire transfer as extracted from the wire transfer subject'; 844 COMMENT ON COLUMN merchant_transfers.wtid IS 'Unique wire transfer identifier (or at least, should be unique by protocol) as selected by the exchange and extracted from the wire transfer subject'; 845 COMMENT ON COLUMN merchant_transfers.credit_amount IS 'actual value of the confirmed wire transfer'; 846 COMMENT ON COLUMN merchant_transfers.account_serial IS 'Merchant bank account that received this wire transfer; also implies the merchant instance implicated by the wire transfer'; 847 COMMENT ON COLUMN merchant_transfers.bank_serial_id IS 'Row ID of the wire transfer from the automated import; NULL if not available (like when a human manually imported the transfer)'; 848 COMMENT ON COLUMN merchant_transfers.expected IS 'True if this wire transfer was expected (has matching entry in merchant_expected_transfers); set automatically via INSERT TRIGGER merchant_transfers_insert_trigger'; 849 COMMENT ON COLUMN merchant_transfers.execution_time IS 'Time when the merchant transfer was added and thus roughly received in our bank account'; 850 851 -- =================================================================== 852 -- Tier 2: tables referencing Tier 1 (and earlier) tables. 853 -- =================================================================== 854 855 CREATE TABLE merchant_deposits ( 856 deposit_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 857 coin_offset INT4 NOT NULL, 858 deposit_confirmation_serial INT8 NOT NULL, 859 coin_pub BYTEA NOT NULL, 860 coin_sig BYTEA NOT NULL, 861 amount_with_fee merchant.taler_amount_currency NOT NULL, 862 deposit_fee merchant.taler_amount_currency NOT NULL, 863 refund_fee merchant.taler_amount_currency NOT NULL, 864 settlement_retry_needed BOOLEAN DEFAULT TRUE, 865 settlement_retry_time INT8 DEFAULT 0, 866 settlement_last_http_status INT4, 867 settlement_last_ec INT4, 868 settlement_last_detail TEXT, 869 settlement_wtid BYTEA, 870 settlement_coin_contribution merchant.taler_amount_currency, 871 settlement_expected_credit_serial INT8, 872 signkey_serial INT8, 873 settlement_exchange_sig BYTEA, 874 CONSTRAINT merchant_deposits_coin_pub_check CHECK ((LENGTH(coin_pub) = 32)), 875 CONSTRAINT merchant_deposits_coin_sig_check CHECK ((LENGTH(coin_sig) = 64)), 876 CONSTRAINT merchant_deposits_settlement_exchange_sig_check CHECK ((LENGTH(settlement_exchange_sig) = 64)), 877 CONSTRAINT merchant_deposits_settlement_wtid_check CHECK ((LENGTH(settlement_wtid) = 32)), 878 UNIQUE (deposit_confirmation_serial, coin_pub), 879 CONSTRAINT merchant_deposits_deposit_confirmation_serial_fkey 880 FOREIGN KEY (deposit_confirmation_serial) 881 REFERENCES merchant_deposit_confirmations(deposit_confirmation_serial) ON DELETE CASCADE, 882 CONSTRAINT merchant_deposits_settlement_expected_credit_serial_fkey 883 FOREIGN KEY (settlement_expected_credit_serial) 884 REFERENCES merchant_expected_transfers(expected_credit_serial), 885 CONSTRAINT merchant_deposits_signkey_serial_fkey 886 FOREIGN KEY (signkey_serial) 887 REFERENCES merchant.merchant_exchange_signing_keys(signkey_serial) ON DELETE CASCADE 888 ); 889 COMMENT ON TABLE merchant_deposits IS 'Table with the deposit details for each coin we deposited at the exchange'; 890 COMMENT ON COLUMN merchant_deposits.coin_offset IS 'Offset of this coin in the batch'; 891 COMMENT ON COLUMN merchant_deposits.deposit_confirmation_serial IS 'Reference to the deposit confirmation of the exchange'; 892 COMMENT ON COLUMN merchant_deposits.coin_pub IS 'Public key of the coin that was deposited'; 893 COMMENT ON COLUMN merchant_deposits.coin_sig IS 'Signature by the coin over the deposit request'; 894 COMMENT ON COLUMN merchant_deposits.amount_with_fee IS 'Total amount (incl. fee) of the coin that was deposited'; 895 COMMENT ON COLUMN merchant_deposits.deposit_fee IS 'Deposit fee (for this coin) that was paid'; 896 COMMENT ON COLUMN merchant_deposits.refund_fee IS 'How high would the refund fee be (for this coin)'; 897 COMMENT ON COLUMN merchant_deposits.settlement_retry_needed IS 'True if we should ask the exchange in the future about the settlement'; 898 COMMENT ON COLUMN merchant_deposits.settlement_retry_time IS 'When should we next ask the exchange about the settlement wire transfer for this coin, initially set to the wire transfer deadline plus a bit of slack'; 899 COMMENT ON COLUMN merchant_deposits.settlement_last_http_status IS 'HTTP status of our last inquiry with the exchange for this deposit, NULL if we never inquired, 0 on timeout'; 900 COMMENT ON COLUMN merchant_deposits.settlement_last_ec IS 'Taler error code for our last inquiry with the exchange for this deposit, NULL if we never inquired, 0 on success'; 901 COMMENT ON COLUMN merchant_deposits.settlement_last_detail IS 'Taler error detail for our last inquiry with the exchange for this deposit, NULL if we never inquired or on success'; 902 COMMENT ON COLUMN merchant_deposits.settlement_wtid IS 'Wire transfer identifier of the transfer of the exchange to this merchant settling the payment'; 903 COMMENT ON COLUMN merchant_deposits.settlement_coin_contribution IS 'Contribution of this coin to the overall wire transfer made by the exchange as claimed by exchange_sig; should match amount_with_fee minus deposit_fee, NULL if we did not get a reply from the exchange'; 904 COMMENT ON COLUMN merchant_deposits.settlement_expected_credit_serial IS 'Identifies the expected wire transfer from the exchange to the merchant that settled the deposit of coin, NULL if unknown'; 905 COMMENT ON COLUMN merchant_deposits.signkey_serial IS 'Identifies the online signing key of the exchange used to make the exchange_sig, NULL for none'; 906 COMMENT ON COLUMN merchant_deposits.settlement_exchange_sig IS 'Exchange signature of purpose TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE, NULL if we did not get such an exchange signature'; 907 CREATE INDEX merchant_deposits_by_deposit_confirmation 908 ON merchant_deposits (deposit_confirmation_serial); 909 CREATE INDEX merchant_deposits_by_settlement_open 910 ON merchant_deposits (settlement_retry_time) 911 WHERE settlement_retry_needed; 912 COMMENT ON INDEX merchant_deposits_by_settlement_open IS 'For select_open_deposit_settlements'; 913 914 CREATE TABLE merchant_inventory_locks ( 915 product_serial INT8 NOT NULL, 916 lock_uuid BYTEA NOT NULL, 917 total_locked INT8 NOT NULL, 918 expiration INT8 NOT NULL, 919 total_locked_frac INT4 DEFAULT 0 NOT NULL, 920 CONSTRAINT merchant_inventory_locks_lock_uuid_check CHECK ((LENGTH(lock_uuid) = 16)), 921 CONSTRAINT merchant_inventory_locks_product_serial_fkey 922 FOREIGN KEY (product_serial) 923 REFERENCES merchant_inventory(product_serial) ON DELETE CASCADE 924 ); 925 COMMENT ON TABLE merchant_inventory_locks IS 'locks on inventory helt by shopping carts; note that locks MAY not be honored if merchants increase total_lost for inventory'; 926 COMMENT ON COLUMN merchant_inventory_locks.product_serial IS 'identifies the inventory product being locked'; 927 COMMENT ON COLUMN merchant_inventory_locks.lock_uuid IS 'unique identifier for the lock'; 928 COMMENT ON COLUMN merchant_inventory_locks.total_locked IS 'how many units of the product does this lock reserve'; 929 COMMENT ON COLUMN merchant_inventory_locks.expiration IS 'when does this lock automatically expire (if no order is created)'; 930 COMMENT ON COLUMN merchant_inventory_locks.total_locked_frac IS 'Fractional part of locked stock in units of 1/1000000 of the base value'; 931 CREATE INDEX merchant_inventory_locks_by_expiration 932 ON merchant_inventory_locks (expiration); 933 CREATE INDEX merchant_inventory_locks_by_uuid 934 ON merchant_inventory_locks (lock_uuid); 935 936 CREATE TABLE merchant_issued_tokens ( 937 issued_token_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 938 h_contract_terms BYTEA NOT NULL, 939 token_family_key_serial INT8, 940 blind_sig BYTEA NOT NULL, 941 CONSTRAINT merchant_issued_tokens_h_contract_terms_check CHECK ((LENGTH(h_contract_terms) = 64)), 942 CONSTRAINT merchant_issued_tokens_token_family_key_serial_fkey 943 FOREIGN KEY (token_family_key_serial) 944 REFERENCES merchant_token_family_keys(token_family_key_serial) ON DELETE CASCADE 945 ); 946 COMMENT ON TABLE merchant_issued_tokens IS 'Tokens that have been (blindly) issued to customers.'; 947 COMMENT ON COLUMN merchant_issued_tokens.h_contract_terms IS 'This is no foreign key by design.'; 948 COMMENT ON COLUMN merchant_issued_tokens.token_family_key_serial IS 'Token family key to which the spent token belongs.'; 949 COMMENT ON COLUMN merchant_issued_tokens.blind_sig IS 'Blind signature made with token issue key to prove validity of token.'; 950 951 CREATE TABLE merchant_order_locks ( 952 product_serial INT8 NOT NULL, 953 total_locked INT8 NOT NULL, 954 order_serial INT8 NOT NULL, 955 total_locked_frac INT4 DEFAULT 0 NOT NULL, 956 CONSTRAINT merchant_order_locks_order_serial_fkey 957 FOREIGN KEY (order_serial) 958 REFERENCES merchant_orders(order_serial) ON DELETE CASCADE, 959 CONSTRAINT merchant_order_locks_product_serial_fkey 960 FOREIGN KEY (product_serial) 961 REFERENCES merchant_inventory(product_serial) 962 ); 963 COMMENT ON TABLE merchant_order_locks IS 'locks on orders awaiting claim and payment; note that locks MAY not be honored if merchants increase total_lost for inventory'; 964 COMMENT ON COLUMN merchant_order_locks.total_locked IS 'how many units of the product does this lock reserve'; 965 COMMENT ON COLUMN merchant_order_locks.order_serial IS 'order for which the lock applies'; 966 COMMENT ON COLUMN merchant_order_locks.total_locked_frac IS 'Fractional part of locked stock associated with orders in units of 1/1000000 of the base value'; 967 CREATE INDEX merchant_orders_locks_by_order_and_product 968 ON merchant_order_locks (order_serial, product_serial); 969 970 CREATE TABLE merchant_product_categories ( 971 category_serial INT8 NOT NULL, 972 product_serial INT8 NOT NULL, 973 CONSTRAINT merchant_product_categories_category_serial_fkey 974 FOREIGN KEY (category_serial) 975 REFERENCES merchant_categories(category_serial) ON DELETE CASCADE, 976 CONSTRAINT merchant_product_categories_product_serial_fkey 977 FOREIGN KEY (product_serial) 978 REFERENCES merchant_inventory(product_serial) ON DELETE CASCADE 979 ); 980 COMMENT ON TABLE merchant_product_categories IS 'N:M map from products to categories (a product can be in any number of categories, including zero)'; 981 COMMENT ON COLUMN merchant_product_categories.category_serial IS 'Reference to a category the product is part of'; 982 COMMENT ON COLUMN merchant_product_categories.product_serial IS 'Reference to a product which is in the given category'; 983 CREATE INDEX merchant_categories_by_category 984 ON merchant_product_categories (category_serial); 985 CREATE INDEX merchant_categories_by_product 986 ON merchant_product_categories (product_serial); 987 988 CREATE TABLE merchant_refund_proofs ( 989 refund_serial INT8 NOT NULL PRIMARY KEY, 990 exchange_sig BYTEA NOT NULL, 991 signkey_serial INT8 NOT NULL, 992 CONSTRAINT merchant_refund_proofs_exchange_sig_check CHECK ((LENGTH(exchange_sig) = 64)), 993 CONSTRAINT merchant_refund_proofs_refund_serial_fkey 994 FOREIGN KEY (refund_serial) 995 REFERENCES merchant_refunds(refund_serial) ON DELETE CASCADE, 996 CONSTRAINT merchant_refund_proofs_signkey_serial_fkey 997 FOREIGN KEY (signkey_serial) 998 REFERENCES merchant.merchant_exchange_signing_keys(signkey_serial) ON DELETE CASCADE 999 ); 1000 COMMENT ON TABLE merchant_refund_proofs IS 'Refunds confirmed by the exchange (not all approved refunds are grabbed by the wallet)'; 1001 COMMENT ON COLUMN merchant_refund_proofs.refund_serial IS 'refund process to which this proof applies'; 1002 COMMENT ON COLUMN merchant_refund_proofs.exchange_sig IS 'signature by the exchange confirming the refund'; 1003 COMMENT ON COLUMN merchant_refund_proofs.signkey_serial IS 'reference to the public key of the exchange by which exchange_sig was made'; 1004 1005 CREATE TABLE merchant_used_tokens ( 1006 spent_token_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 1007 h_contract_terms BYTEA NOT NULL, 1008 token_family_key_serial INT8, 1009 token_pub BYTEA NOT NULL UNIQUE, 1010 token_sig BYTEA NOT NULL, 1011 blind_sig BYTEA NOT NULL, 1012 CONSTRAINT merchant_spent_tokens_h_contract_terms_check CHECK ((LENGTH(h_contract_terms) = 64)), 1013 CONSTRAINT merchant_spent_tokens_token_pub_check CHECK ((LENGTH(token_pub) = 32)), 1014 CONSTRAINT merchant_spent_tokens_token_sig_check CHECK ((LENGTH(token_sig) = 64)), 1015 CONSTRAINT merchant_spent_tokens_token_family_key_serial_fkey 1016 FOREIGN KEY (token_family_key_serial) 1017 REFERENCES merchant_token_family_keys(token_family_key_serial) ON DELETE CASCADE 1018 ); 1019 COMMENT ON TABLE merchant_used_tokens IS 'Tokens that have been spent by customers.'; 1020 COMMENT ON COLUMN merchant_used_tokens.h_contract_terms IS 'This is no foreign key by design.'; 1021 COMMENT ON COLUMN merchant_used_tokens.token_family_key_serial IS 'Token family to which the spent token belongs.'; 1022 COMMENT ON COLUMN merchant_used_tokens.token_pub IS 'Public key of the spent token.'; 1023 COMMENT ON COLUMN merchant_used_tokens.token_sig IS 'Signature that the token was spent on specified order.'; 1024 COMMENT ON COLUMN merchant_used_tokens.blind_sig IS 'Blind signature for the spent token to prove validity of token.'; 1025 1026 CREATE TABLE merchant_statistic_interval_amount ( 1027 imeta_serial_id INT8 NOT NULL, 1028 event_delimiter INT8 NOT NULL, 1029 range INT8 NOT NULL, 1030 curr character varying(12) NOT NULL, 1031 cumulative_value INT8 NOT NULL, 1032 cumulative_frac INT4 NOT NULL, 1033 PRIMARY KEY (imeta_serial_id, curr, range), 1034 CONSTRAINT merchant_statistic_interval_amount_event_delimiter_fkey 1035 FOREIGN KEY (event_delimiter) 1036 REFERENCES merchant_statistic_amount_event(aevent_serial_id) ON DELETE RESTRICT, 1037 CONSTRAINT merchant_statistic_interval_amount_imeta_serial_id_fkey 1038 FOREIGN KEY (imeta_serial_id) 1039 REFERENCES merchant_statistic_interval_meta(imeta_serial_id) ON DELETE CASCADE 1040 ); 1041 COMMENT ON TABLE merchant_statistic_interval_amount IS 'various amount statistics (in various currencies) being tracked'; 1042 COMMENT ON COLUMN merchant_statistic_interval_amount.imeta_serial_id IS 'identifies what the statistic is about'; 1043 COMMENT ON COLUMN merchant_statistic_interval_amount.event_delimiter IS 'FIXME'; 1044 COMMENT ON COLUMN merchant_statistic_interval_amount.range IS 'for which range is this the counter; note that the cumulative_number excludes the values already stored in smaller ranges'; 1045 COMMENT ON COLUMN merchant_statistic_interval_amount.curr IS 'currency which this statistic is tracking the amount for'; 1046 COMMENT ON COLUMN merchant_statistic_interval_amount.cumulative_value IS 'amount in the respective currency, non-fractional amount value'; 1047 COMMENT ON COLUMN merchant_statistic_interval_amount.cumulative_frac IS 'amount in the respective currency, fraction in units of 1/100000000 of the base value'; 1048 1049 CREATE TABLE merchant_statistic_interval_counter ( 1050 imeta_serial_id INT8 NOT NULL, 1051 range INT8 NOT NULL, 1052 event_delimiter INT8 NOT NULL, 1053 cumulative_number INT8 NOT NULL, 1054 PRIMARY KEY (imeta_serial_id, range), 1055 CONSTRAINT merchant_statistic_interval_counter_event_delimiter_fkey 1056 FOREIGN KEY (event_delimiter) 1057 REFERENCES merchant_statistic_counter_event(nevent_serial_id) ON DELETE RESTRICT, 1058 CONSTRAINT merchant_statistic_interval_counter_imeta_serial_id_fkey 1059 FOREIGN KEY (imeta_serial_id) 1060 REFERENCES merchant_statistic_interval_meta(imeta_serial_id) ON DELETE CASCADE 1061 ); 1062 COMMENT ON TABLE merchant_statistic_interval_counter IS 'various numeric statistics (cumulative counters) being tracked'; 1063 COMMENT ON COLUMN merchant_statistic_interval_counter.imeta_serial_id IS 'identifies what the statistic is about'; 1064 COMMENT ON COLUMN merchant_statistic_interval_counter.range IS 'for which range is this the counter; note that the cumulative_number excludes the values already stored in smaller ranges'; 1065 COMMENT ON COLUMN merchant_statistic_interval_counter.event_delimiter IS 'determines the last event currently included in the interval'; 1066 COMMENT ON COLUMN merchant_statistic_interval_counter.cumulative_number IS 'aggregate (sum) of tracked by the statistic; what exactly is tracked is determined by the keyword'; 1067 1068 CREATE TABLE merchant_transfer_signatures ( 1069 expected_credit_serial INT8 NOT NULL PRIMARY KEY, 1070 signkey_serial INT8 NOT NULL, 1071 wire_fee merchant.taler_amount_currency NOT NULL, 1072 credit_amount merchant.taler_amount_currency NOT NULL, 1073 execution_time INT8 NOT NULL, 1074 exchange_sig BYTEA NOT NULL, 1075 CONSTRAINT merchant_transfer_signatures_exchange_sig_check CHECK ((LENGTH(exchange_sig) = 64)), 1076 CONSTRAINT merchant_transfer_signatures_expected_credit_serial_fkey 1077 FOREIGN KEY (expected_credit_serial) 1078 REFERENCES merchant_expected_transfers(expected_credit_serial) ON DELETE CASCADE, 1079 CONSTRAINT merchant_transfer_signatures_signkey_serial_fkey 1080 FOREIGN KEY (signkey_serial) 1081 REFERENCES merchant.merchant_exchange_signing_keys(signkey_serial) ON DELETE CASCADE 1082 ); 1083 COMMENT ON TABLE merchant_transfer_signatures IS 'table represents the main information returned from the /transfer request to the exchange.'; 1084 COMMENT ON COLUMN merchant_transfer_signatures.expected_credit_serial IS 'expected wire transfer this signature is about'; 1085 COMMENT ON COLUMN merchant_transfer_signatures.signkey_serial IS 'Online signing key by the exchange that was used for the exchange_sig signature'; 1086 COMMENT ON COLUMN merchant_transfer_signatures.wire_fee IS 'wire fee charged by the exchange for this transfer'; 1087 COMMENT ON COLUMN merchant_transfer_signatures.credit_amount IS 'actual value of the (aggregated) wire transfer, excluding the wire fee, according to the exchange'; 1088 COMMENT ON COLUMN merchant_transfer_signatures.execution_time IS 'Execution time as claimed by the exchange, roughly matches time seen by merchant'; 1089 COMMENT ON COLUMN merchant_transfer_signatures.exchange_sig IS 'signature by the exchange of purpose TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT'; 1090 1091 -- =================================================================== 1092 -- Tier 3: tables referencing Tier 2 tables. 1093 -- =================================================================== 1094 1095 CREATE TABLE merchant_expected_transfer_to_coin ( 1096 deposit_serial INT8 NOT NULL UNIQUE, 1097 expected_credit_serial INT8 NOT NULL, 1098 offset_in_exchange_list INT8 NOT NULL, 1099 exchange_deposit_value merchant.taler_amount_currency NOT NULL, 1100 exchange_deposit_fee merchant.taler_amount_currency NOT NULL, 1101 CONSTRAINT merchant_expected_transfer_to_coin_deposit_serial_fkey 1102 FOREIGN KEY (deposit_serial) 1103 REFERENCES merchant_deposits(deposit_serial) ON DELETE CASCADE, 1104 CONSTRAINT merchant_expected_transfer_to_coin_expected_credit_serial_fkey 1105 FOREIGN KEY (expected_credit_serial) 1106 REFERENCES merchant_expected_transfers(expected_credit_serial) ON DELETE CASCADE 1107 ); 1108 COMMENT ON TABLE merchant_expected_transfer_to_coin IS 'Mapping of (credit) transfers to (deposited) coins'; 1109 COMMENT ON COLUMN merchant_expected_transfer_to_coin.deposit_serial IS 'Identifies the deposited coin that the wire transfer presumably settles'; 1110 COMMENT ON COLUMN merchant_expected_transfer_to_coin.expected_credit_serial IS 'Identifies the expected wire transfer that settles the given deposited coin'; 1111 COMMENT ON COLUMN merchant_expected_transfer_to_coin.offset_in_exchange_list IS 'The exchange settlement data includes an array of the settled coins; this is the index of the coin in that list, useful to reconstruct the correct sequence of coins as needed to check the exchange signature'; 1112 COMMENT ON COLUMN merchant_expected_transfer_to_coin.exchange_deposit_value IS 'Deposit value as claimed by the exchange, should match our values in merchant_deposits minus refunds'; 1113 COMMENT ON COLUMN merchant_expected_transfer_to_coin.exchange_deposit_fee IS 'Deposit value as claimed by the exchange, should match our values in merchant_deposits'; 1114 CREATE INDEX merchant_transfers_by_credit 1115 ON merchant_expected_transfer_to_coin (expected_credit_serial); 1116 1117 SET LOCAL search_path TO merchant; 1118 END 1119 $OUTER$; 1120 1121 INSERT INTO merchant.instance_fixups 1122 (migration_name 1123 ,version) 1124 VALUES 1125 ('merchant_0036_init' 1126 ,36);