merchant

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

lib.typ (7464B)


      1 // Helper function to format timeframe
      2 #let format_timeframe(d_us) = {
      3   if d_us == "forever" {
      4     "forever"
      5   } else {
      6   let us = int(d_us)
      7   let s = calc.quo(us, 1000000)
      8   let m = calc.quo(s, 60)
      9   let h = calc.quo(m, 60)
     10   let d = calc.quo(h, 24)
     11   let w = calc.quo(d, 7)
     12 
     13   if calc.rem(us, 1000000) == 0 {
     14     if calc.rem(s, 60) == 0 {
     15       if calc.rem(m, 60) == 0 {
     16           if calc.rem(h, 24) == 0 {
     17             if calc.rem(d, 7) == 0 {
     18               str(w) + " week" + if w != 1 { "s" } else { "" }
     19             } else {
     20               str(d) + " day" + if d != 1 { "s" } else { "" }
     21             }
     22           } else {
     23             str(h) + " hour" + if h != 1 { "s" } else { "" }
     24           }
     25         } else {
     26           str(m) + " minute" + if m != 1 { "s" } else { "" }
     27         }
     28       } else {
     29         str(s) + " s"
     30       }
     31     } else {
     32       str(us) + " μs"
     33     }
     34   }
     35 }
     36 
     37 // Helper function to format timestamp; ignores leap seconds (too variable)
     38 // Helper function to format a Taler timestamp object {t_s: ...}
     39 #let format_timestamp(ts) = {
     40   if type(ts) == dictionary and "t_s" in ts {
     41     let t_s = ts.t_s
     42     if t_s == "never" {
     43       "never"
     44     } else {
     45       // Convert Unix timestamp to human-readable format
     46       let seconds = int(t_s)
     47       let days_since_epoch = calc.quo(seconds, 86400)
     48       let remaining_seconds = calc.rem(seconds, 86400)
     49       let hours = calc.quo(remaining_seconds, 3600)
     50       let minutes = calc.quo(calc.rem(remaining_seconds, 3600), 60)
     51       let secs = calc.rem(remaining_seconds, 60)
     52 
     53       // Helper to check if year is leap year
     54       let is_leap(y) = {
     55         calc.rem(y, 4) == 0 and (calc.rem(y, 100) != 0 or calc.rem(y, 400) == 0)
     56       }
     57 
     58       // Calculate year, month, day
     59       let year = 1970
     60       let days_left = days_since_epoch
     61 
     62       // Find the year
     63       let done = false
     64       while not done {
     65         let days_in_year = if is_leap(year) { 366 } else { 365 }
     66         if days_left >= days_in_year {
     67           days_left = days_left - days_in_year
     68           year = year + 1
     69         } else {
     70           done = true
     71         }
     72       }
     73 
     74       // Days in each month
     75       let days_in_months = if is_leap(year) {
     76         (31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
     77       } else {
     78         (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
     79       }
     80 
     81       // Find month and day
     82       let month = 1
     83       for days_in_month in days_in_months {
     84         if days_left >= days_in_month {
     85           days_left = days_left - days_in_month
     86           month = month + 1
     87         } else {
     88           break
     89         }
     90       }
     91       let day = days_left + 1
     92 
     93       // Format with leading zeros
     94       let m_str = if month < 10 { "0" + str(month) } else { str(month) }
     95       let d_str = if day < 10 { "0" + str(day) } else { str(day) }
     96       let h_str = if hours < 10 { "0" + str(hours) } else { str(hours) }
     97       let min_str = if minutes < 10 { "0" + str(minutes) } else { str(minutes) }
     98       let s_str = if secs < 10 { "0" + str(secs) } else { str(secs) }
     99 
    100       str(year) + "-" + m_str + "-" + d_str + " " + h_str + ":" + min_str + ":" + s_str + " UTC"
    101     }
    102   } else {
    103     str(ts)
    104   }
    105 }
    106 
    107 
    108 // Helper function to format timestamp; ignores leap seconds (too variable)
    109 // Coarsen based on r_us (a value in milliseconds).
    110 // If r_us is a minute, do not show seconds.
    111 // If r_us is an hour, do not show minutes or seconds
    112 // If r_us is a day, do not show hours, minutes or seconds.
    113 // If r_us is a month, do not show days.
    114 // If r_us is a year, do not show months.
    115 // If mini is true, truly minify the result only showing
    116 // the unit around the r_us granularity.
    117 #let format_round_timestamp(ts, r_us, mini) = {
    118   if type(ts) == dictionary and "t_s" in ts {
    119     let t_s = ts.t_s
    120     if t_s == "never" {
    121       "never"
    122     } else {
    123       // Convert Unix timestamp to human-readable format
    124       let seconds = int(t_s)
    125       let days_since_epoch = calc.quo(seconds, 86400)
    126       let remaining_seconds = calc.rem(seconds, 86400)
    127       let hours = calc.quo(remaining_seconds, 3600)
    128       let minutes = calc.quo(calc.rem(remaining_seconds, 3600), 60)
    129       let secs = calc.rem(remaining_seconds, 60)
    130 
    131       // Helper to check if year is leap year
    132       let is_leap(y) = {
    133         calc.rem(y, 4) == 0 and (calc.rem(y, 100) != 0 or calc.rem(y, 400) == 0)
    134       }
    135 
    136       // Calculate year, month, day
    137       let year = 1970
    138       let days_left = days_since_epoch
    139 
    140       // Find the year
    141       let done = false
    142       while not done {
    143         let days_in_year = if is_leap(year) { 366 } else { 365 }
    144         if days_left >= days_in_year {
    145           days_left = days_left - days_in_year
    146           year = year + 1
    147         } else {
    148           done = true
    149         }
    150       }
    151 
    152       // Days in each month
    153       let days_in_months = if is_leap(year) {
    154         (31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
    155       } else {
    156         (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
    157       }
    158 
    159       // Find month and day
    160       let month = 1
    161       for days_in_month in days_in_months {
    162         if days_left >= days_in_month {
    163           days_left = days_left - days_in_month
    164           month = month + 1
    165         } else {
    166           break
    167         }
    168       }
    169       let day = days_left + 1
    170 
    171       // Format with leading zeros
    172       let m_str = if month < 10 { "0" + str(month) } else { str(month) }
    173       let d_str = if day < 10 { "0" + str(day) } else { str(day) }
    174       let h_str = if hours < 10 { "0" + str(hours) } else { str(hours) }
    175       let min_str = if minutes < 10 { "0" + str(minutes) } else { str(minutes) }
    176       let s_str = if secs < 10 { "0" + str(secs) } else { str(secs) }
    177 
    178       // Define thresholds in microseconds
    179       let minute_us = 60 * 1000 * 1000
    180       let hour_us = 60 * minute_us
    181       let day_us = 24 * hour_us
    182       let month_us = 30 * day_us  // Approximate: 30 days
    183       let year_us = 365 * day_us  // Approximate: 365 days
    184 
    185       // Build timestamp string based on r_us thresholds
    186       if r_us >= year_us {
    187         // Year or more: show only year
    188         str(year)
    189       } else if r_us >= month_us {
    190         // Month or more: show year and month
    191         if (mini) { m_str } else { str(year) + "-" + m_str }
    192       } else if r_us >= day_us {
    193         // Day or more: show year, month, and day
    194         if (mini) { d_str + "d" } else { str(year) + "-" + m_str + "-" + d_str }
    195       } else if r_us >= hour_us {
    196         // Hour or more: show up to hours
    197         if (mini) { h_str + "h" } else { str(year) + "-" + m_str + "-" + d_str + " " + h_str + ":00 UTC" }
    198       } else if r_us >= minute_us {
    199         // Minute or more: show up to minutes
    200         if (mini) { min_str + "m" } else { str(year) + "-" + m_str + "-" + d_str + " " + h_str + ":" + min_str + " UTC" }
    201       } else {
    202         // Less than a minute: show full precision
    203         if (mini) { s_str + "s" } else { str(year) + "-" + m_str + "-" + d_str + " " + h_str + ":" + min_str + ":" + s_str + " UTC" }
    204       }
    205     }
    206   } else {
    207     str(ts)
    208   }
    209 }
    210 
    211 // Format a Taler amount.
    212 // Taler serialises amounts as a plain string "CURRENCY:VALUE.FRACTION",
    213 // e.g. "EUR:5.50".  If the value is null / none we render a dash.
    214 #let format_amount(a) = {
    215   if a == none {
    216     "-"
    217   } else {
    218     // a is already a human-readable string like "EUR:5.50"
    219     // Note: eventually we want it formatted *nicely*, but this
    220     // needs the currency rendering data, so probably better done
    221     // on the C side (where we don't yet have an amount renderer).
    222     str(a).replace(":", " ")
    223   }
    224 }