taler-mailbox

Service for asynchronous wallet-to-wallet payment messages
Log | Files | Refs | Submodules | README | LICENSE

commit fbf33892e87b4888cfa5bf1882ada66b19b32cbb
parent dd1dcebaff3dbf676e5e445f79e4db84fd3bd3cc
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date:   Mon, 10 Nov 2025 22:46:24 +0100

update with newer api spec

Diffstat:
Minternal/gana/taler_signatures.go | 4++--
Mmailbox.conf.example | 5+++--
Mpkg/rest/mailbox.go | 167++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
3 files changed, 103 insertions(+), 73 deletions(-)

diff --git a/internal/gana/taler_signatures.go b/internal/gana/taler_signatures.go @@ -471,7 +471,7 @@ const ( /** - * Signature over new key set in key update (Gnu Taler) + * Signature for mailbox registration request (Gnu Taler) */ - TalerSignaturePurposeMailboxKeysUpdate = 1552 + TalerSignaturePurposeMailboxRegister = 1552 ) diff --git a/mailbox.conf.example b/mailbox.conf.example @@ -3,8 +3,9 @@ bind_to = localhost:11000 base_url = https://example.com production = false message_body_bytes = 256 -message_response_limit -message_fee = KUDOS:0 +message_response_limit = 50 +registration_fee = KUDOS:0 +registration_period = 8760h [mailbox-pq] host = localhost diff --git a/pkg/rest/mailbox.go b/pkg/rest/mailbox.go @@ -108,8 +108,8 @@ type Mailbox struct { // Base URL BaseUrl string - // MessageFee for sending message - MessageFee *talerutil.Amount + // Registration fee for registering mailbox + RegistrationFee *talerutil.Amount // How many messages will a single response // contain at maximum. @@ -117,6 +117,12 @@ type Mailbox struct { // Logger Logger *log.Logger + + // Currency Spec + CurrencySpec talerutil.CurrencySpecification + + // Registration period + RegistrationPeriod time.Duration } type RelativeTime struct { @@ -137,9 +143,6 @@ type VersionResponse struct { // Name of the protocol. Name string `json:"name"` // "taler-mailbox" - // fee for one month of registration - MessageFee string `json:"message_fee"` - // Fixed size of message bodies MessageBodyBytes int64 `json:"message_body_bytes"` @@ -150,6 +153,15 @@ type VersionResponse struct { // How many messages will a single response // contain at maximum. MessageResponseLimit uint64 `json:"message_response_limit"` + + // How much is the cost of a single + // registration period of a mailbox + // May be 0 for a free registration. + RegistrationFee string `json:"registration_fee"` + + // How long is a registration period + // a mailbox + RegistrationPeriod int64 `json:"registration_period"` } type MailboxMessageKeys struct { @@ -201,7 +213,6 @@ type KeyUpdateRequest struct { Signature string `json:"signature"` } - // MessageDeletionRequest is used to request the deletion of already received // messages from the mailbox. type MessageDeletionRequest struct { @@ -254,8 +265,9 @@ func (m *Mailbox) configResponse(w http.ResponseWriter, r *http.Request) { Name: "taler-mailbox", MessageBodyBytes: m.MessageBodyBytes, MessageResponseLimit: m.MessageResponseLimit, - MessageFee: m.MessageFee.String(), - DeliveryPeriod: RelativeTime{D_us: uint64(dp.Microseconds())}, + RegistrationFee: m.RegistrationFee.String(), + DeliveryPeriod: RelativeTime{D_us: uint64(dp.Microseconds())}, + RegistrationPeriod: m.RegistrationPeriod.Microseconds(), } w.Header().Set("Content-Type", "application/json") response, _ := json.Marshal(cfg) @@ -312,64 +324,8 @@ func (m *Mailbox) sendMessageResponse(w http.ResponseWriter, r *http.Request) { } entry.HMailbox = vars["h_mailbox"] entry.Body = body - if m.MessageFee.IsZero() { - m.Db.Save(&entry) - w.WriteHeader(http.StatusNoContent) - return - } - - // Check if order exists and was paid already. - h := sha256.New() - h.Sum(body) - orderId := util.Base32CrockfordEncode(h.Sum(nil)) - httpStatus, paymentStatus, payto, paytoErr := m.Merchant.IsOrderPaid(orderId) - if paytoErr != nil { - fmt.Println(paytoErr) - w.WriteHeader(http.StatusInternalServerError) - return - } - switch httpStatus { - case http.StatusNotFound: - // Not found. Create new order. - var order merchant.CommonOrder - order.OrderId = orderId - order.Amount = m.MessageFee.String() - order.Summary = "Mailbox message dispatch" - order.MerchantBaseUrl = m.BaseUrl - _, newOrderErr := m.Merchant.CreateOrder(order) - if newOrderErr != nil { - fmt.Println(newOrderErr) - w.WriteHeader(http.StatusInternalServerError) - return - } - // Check for order again to get payto. - _, _, payto, paytoErr = m.Merchant.IsOrderPaid(orderId) - if paytoErr != nil { - fmt.Println(paytoErr) - w.WriteHeader(http.StatusInternalServerError) - return - } - if payto == "" { - fmt.Println(paytoErr) - w.WriteHeader(http.StatusInternalServerError) - return - } - w.WriteHeader(http.StatusPaymentRequired) - w.Header().Set("Taler", payto) - return - case http.StatusOK: - // Check if it was actually paid. - if paymentStatus == "paid" { - m.Db.Save(&entry) - w.WriteHeader(http.StatusNoContent) - return - } - w.WriteHeader(http.StatusPaymentRequired) - if payto != "" { - w.Header().Set("Taler", payto) - } - return - } + m.Db.Save(&entry) + w.WriteHeader(http.StatusNoContent) } func (m *Mailbox) getKeysResponse(w http.ResponseWriter, r *http.Request) { @@ -415,6 +371,60 @@ func (m *Mailbox) updateKeysResponse(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadRequest) return } + if !m.RegistrationFee.IsZero() { + + // Check if order exists and was paid already. + h := sha256.New() + h.Sum(pk) + orderID := util.Base32CrockfordEncode(h.Sum(nil)) + httpStatus, paymentStatus, payto, paytoErr := m.Merchant.IsOrderPaid(orderID) + if paytoErr != nil { + fmt.Println(paytoErr) + w.WriteHeader(http.StatusInternalServerError) + return + } + switch httpStatus { + case http.StatusNotFound: + // Not found. Create new order. + var order merchant.CommonOrder + order.OrderId = orderID + order.Amount = m.RegistrationFee.String() + order.Summary = "Mailbox registration" + order.MerchantBaseUrl = m.BaseUrl + _, newOrderErr := m.Merchant.CreateOrder(order) + if newOrderErr != nil { + fmt.Println(newOrderErr) + w.WriteHeader(http.StatusInternalServerError) + return + } + // Check for order again to get payto. + _, _, payto, paytoErr = m.Merchant.IsOrderPaid(orderID) + if paytoErr != nil { + fmt.Println(paytoErr) + w.WriteHeader(http.StatusInternalServerError) + return + } + if payto == "" { + fmt.Println(paytoErr) + w.WriteHeader(http.StatusInternalServerError) + return + } + w.WriteHeader(http.StatusPaymentRequired) + w.Header().Set("Taler", payto) + return + case http.StatusOK: + // Check if it was actually paid. + if paymentStatus == "paid" { + break + } + default: + w.WriteHeader(http.StatusPaymentRequired) + if payto != "" { + w.Header().Set("Taler", payto) + } + return + } + } var expNbo [8]byte var signedMsg [72]byte binary.BigEndian.PutUint64(expNbo[:], msg.Keys.Expiration.T_s) @@ -604,13 +614,19 @@ func (m *Mailbox) Initialize(cfg MailboxConfig) { m.BaseUrl = cfg.Ini.Section("mailbox").Key("base_url").MustString("https://example.com") m.MessageBodyBytes = cfg.Ini.Section("mailbox").Key("message_body_bytes").MustInt64(256) m.MessageResponseLimit = cfg.Ini.Section("mailbox").Key("message_response_limit").MustUint64(50) - // FIXME actual cost - fee, err := talerutil.ParseAmount(cfg.Ini.Section("mailbox").Key("message_fee").MustString("KUDOS:1")) + registrationPeriodStr := cfg.Ini.Section("mailbox").Key("registration_period").MustString("8760h") + registrationPeriod, err := time.ParseDuration(registrationPeriodStr) + if err != nil { + fmt.Printf("Failed to parse duration: %v", err) + os.Exit(1) + } + m.RegistrationPeriod = registrationPeriod + fee, err := talerutil.ParseAmount(cfg.Ini.Section("mailbox").Key("registration_fee").MustString("KUDOS:0")) if err != nil { fmt.Printf("Failed to parse cost: %v", err) os.Exit(1) } - m.MessageFee = fee + m.RegistrationFee = fee _db, err := gorm.Open(cfg.Db, &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) @@ -626,5 +642,18 @@ func (m *Mailbox) Initialize(cfg MailboxConfig) { } m.Merchant = cfg.Merchant + if !fee.IsZero() { + merchConfig, err := m.Merchant.GetConfig() + if err != nil { + fmt.Printf("Failed to get merchant config: %v", err) + os.Exit(1) + } + currencySpec, currencySupported := merchConfig.Currencies[fee.Currency] + for !currencySupported { + fmt.Printf("Currency `%s' not supported by merchant!\n", fee.Currency) + os.Exit(1) + } + m.CurrencySpec = currencySpec + } m.setupHandlers() }