diff options
author | Martin Schanzenbach <schanzen@gnunet.org> | 2023-12-06 14:52:13 +0100 |
---|---|---|
committer | Martin Schanzenbach <schanzen@gnunet.org> | 2023-12-06 14:52:13 +0100 |
commit | a84002ca1ad4f70cc7f6c5105424f7a8ab1af6d8 (patch) | |
tree | 00563ebaf7e6c17876d5b6904e090ab78215905c | |
parent | 86fb4f92dd0a6624e3cd7e7f25781c011483c642 (diff) | |
download | gnunet-gns-registrar-a84002ca1ad4f70cc7f6c5105424f7a8ab1af6d8.tar.gz gnunet-gns-registrar-a84002ca1ad4f70cc7f6c5105424f7a8ab1af6d8.zip |
Towards paying with the taler demo
-rw-r--r-- | gns-registrar.conf | 4 | ||||
-rw-r--r-- | go.mod | 3 | ||||
-rw-r--r-- | pkg/rest/gnsregistrar.go | 233 | ||||
-rw-r--r-- | web/templates/buy.html | 22 | ||||
-rw-r--r-- | web/templates/name.html | 12 | ||||
-rw-r--r-- | web/templates/names.html | 20 |
6 files changed, 207 insertions, 87 deletions
diff --git a/gns-registrar.conf b/gns-registrar.conf index e929f1b..6b12eaa 100644 --- a/gns-registrar.conf +++ b/gns-registrar.conf | |||
@@ -9,8 +9,8 @@ default_doc_lang = en | |||
9 | default_tos_path = terms/ | 9 | default_tos_path = terms/ |
10 | default_pp_path = privacy/ | 10 | default_pp_path = privacy/ |
11 | supported_doc_filetypes = text/html application/pdf application/epub application/xml text/plain | 11 | supported_doc_filetypes = text/html application/pdf application/epub application/xml text/plain |
12 | merchant_baseurl_private = http://merchant.taldir/instances/myInstance | 12 | merchant_baseurl_private = https://backend.demo.taler.net |
13 | merchant_token = superSecretToken | 13 | merchant_token = sandbox |
14 | registrar_landing = web/templates/landing.html | 14 | registrar_landing = web/templates/landing.html |
15 | registrar_name = web/templates/name.html | 15 | registrar_name = web/templates/name.html |
16 | registration_failed = templates/registration_failed.html | 16 | registration_failed = templates/registration_failed.html |
@@ -4,6 +4,7 @@ go 1.18 | |||
4 | 4 | ||
5 | require ( | 5 | require ( |
6 | github.com/gorilla/mux v1.8.0 // indirect | 6 | github.com/gorilla/mux v1.8.0 // indirect |
7 | github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect | ||
7 | gopkg.in/ini.v1 v1.67.0 // indirect | 8 | gopkg.in/ini.v1 v1.67.0 // indirect |
8 | taler.net/taler-go.git v0.0.0-20220719135513-36eb87bf37a3 // indirect | 9 | taler.net/taler-go.git v0.0.0-20231206131418-346a54653b41 // indirect |
9 | ) | 10 | ) |
diff --git a/pkg/rest/gnsregistrar.go b/pkg/rest/gnsregistrar.go index b243e3e..7d3f6c4 100644 --- a/pkg/rest/gnsregistrar.go +++ b/pkg/rest/gnsregistrar.go | |||
@@ -20,7 +20,9 @@ package gnsregistrar | |||
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "bytes" | 22 | "bytes" |
23 | "encoding/base64" | ||
23 | "encoding/json" | 24 | "encoding/json" |
25 | "errors" | ||
24 | "fmt" | 26 | "fmt" |
25 | "html/template" | 27 | "html/template" |
26 | "io" | 28 | "io" |
@@ -28,15 +30,19 @@ import ( | |||
28 | "os" | 30 | "os" |
29 | "time" | 31 | "time" |
30 | 32 | ||
33 | "github.com/skip2/go-qrcode" | ||
31 | "github.com/gorilla/mux" | 34 | "github.com/gorilla/mux" |
32 | "gopkg.in/ini.v1" | 35 | "gopkg.in/ini.v1" |
33 | "taler.net/taler-go.git/pkg/merchant" | 36 | "taler.net/taler-go.git/pkg/merchant" |
37 | talerutil "taler.net/taler-go.git/pkg/util" | ||
34 | ) | 38 | ) |
35 | 39 | ||
36 | 40 | ||
37 | type RegistrationMetadata struct { | 41 | type RegistrationMetadata struct { |
38 | Expiration uint64 `json:"expiration"` | 42 | Expiration uint64 `json:"expiration"` |
39 | Paid bool `json:"paid"` | 43 | Paid bool `json:"paid"` |
44 | OrderID string `json:"order_id"` | ||
45 | NeedsPaymentUntil time.Time `json:"needs_payment_until"` | ||
40 | } | 46 | } |
41 | 47 | ||
42 | type IdentityInfo struct { | 48 | type IdentityInfo struct { |
@@ -85,8 +91,8 @@ type Registrar struct { | |||
85 | // name page | 91 | // name page |
86 | NameTpl *template.Template | 92 | NameTpl *template.Template |
87 | 93 | ||
88 | // all names page | 94 | // buy names page |
89 | AllNamesTpl *template.Template | 95 | BuyTpl *template.Template |
90 | 96 | ||
91 | // Merchant object | 97 | // Merchant object |
92 | Merchant merchant.Merchant | 98 | Merchant merchant.Merchant |
@@ -97,6 +103,9 @@ type Registrar struct { | |||
97 | // Registration expiration (NOT record expiration!) | 103 | // Registration expiration (NOT record expiration!) |
98 | RelativeRegistrationExpiration time.Duration | 104 | RelativeRegistrationExpiration time.Duration |
99 | 105 | ||
106 | // Payment expiration (time you have to pay for registration) | ||
107 | PaymentExpiration time.Duration | ||
108 | |||
100 | // Name of our root zone | 109 | // Name of our root zone |
101 | RootZoneName string | 110 | RootZoneName string |
102 | 111 | ||
@@ -113,10 +122,8 @@ type Registrar struct { | |||
113 | RegistrationPolicy string | 122 | RegistrationPolicy string |
114 | 123 | ||
115 | // Cost for a registration | 124 | // Cost for a registration |
116 | RegistrationCost uint64 | 125 | RegistrationCost *talerutil.Amount |
117 | 126 | ||
118 | // Currency to use | ||
119 | RegistrationCostCurrency string | ||
120 | } | 127 | } |
121 | 128 | ||
122 | type VersionResponse struct { | 129 | type VersionResponse struct { |
@@ -149,7 +156,7 @@ func (t *Registrar) landingPage(w http.ResponseWriter, r *http.Request) { | |||
149 | func (t *Registrar) registerName(w http.ResponseWriter, r *http.Request) { | 156 | func (t *Registrar) registerName(w http.ResponseWriter, r *http.Request) { |
150 | vars := mux.Vars(r) | 157 | vars := mux.Vars(r) |
151 | var namestoreRequest NamestoreRecord | 158 | var namestoreRequest NamestoreRecord |
152 | var delegationRecord RecordData | 159 | var delegationRecord RecordData |
153 | var metadataRecord RecordData | 160 | var metadataRecord RecordData |
154 | var gnunetError GnunetError | 161 | var gnunetError GnunetError |
155 | var registrationMetadata RegistrationMetadata | 162 | var registrationMetadata RegistrationMetadata |
@@ -173,45 +180,193 @@ func (t *Registrar) registerName(w http.ResponseWriter, r *http.Request) { | |||
173 | Paid: false, | 180 | Paid: false, |
174 | Expiration: uint64(time.Now().Add(t.RelativeRegistrationExpiration).UnixMicro()), | 181 | Expiration: uint64(time.Now().Add(t.RelativeRegistrationExpiration).UnixMicro()), |
175 | } | 182 | } |
176 | metadataRecordValue, err := json.Marshal(registrationMetadata) | 183 | metadataRecordValue, err := json.Marshal(registrationMetadata) |
177 | if nil != err { | 184 | if nil != err { |
178 | http.Redirect(w, r, "/name?label="+vars["label"] + "&error=Registration failed", http.StatusSeeOther) | 185 | http.Redirect(w, r, "/name?label="+vars["label"] + "&error=Registration failed", http.StatusSeeOther) |
179 | return | 186 | return |
180 | } | 187 | } |
181 | metadataRecord.Value = string(metadataRecordValue) | 188 | metadataRecord.Value = string(metadataRecordValue) |
182 | namestoreRequest.RecordName = vars["label"] | 189 | namestoreRequest.RecordName = vars["label"] |
183 | namestoreRequest.Records = []RecordData{delegationRecord,metadataRecord} | 190 | namestoreRequest.Records = []RecordData{delegationRecord,metadataRecord} |
184 | reqString, _ := json.Marshal(namestoreRequest) | 191 | reqString, _ := json.Marshal(namestoreRequest) |
185 | // FIXME handle errors here | 192 | // FIXME handle errors here |
186 | fmt.Println(namestoreRequest) | 193 | fmt.Println(namestoreRequest) |
187 | resp, err := http.Post(t.GnunetUrl+"/namestore/" + t.RootZoneName, "application/json", bytes.NewBuffer(reqString)) | 194 | resp, err := http.Post(t.GnunetUrl+"/namestore/" + t.RootZoneName, "application/json", bytes.NewBuffer(reqString)) |
188 | resp.Body.Close() | 195 | resp.Body.Close() |
189 | if http.StatusNoContent != resp.StatusCode { | 196 | if http.StatusNoContent != resp.StatusCode { |
190 | fmt.Printf("Got error: %d\n", resp.StatusCode) | 197 | fmt.Printf("Got error: %d\n", resp.StatusCode) |
191 | json.NewDecoder(resp.Body).Decode(&gnunetError) | 198 | json.NewDecoder(resp.Body).Decode(&gnunetError) |
192 | fmt.Println(gnunetError.Code) | 199 | fmt.Println(gnunetError.Code) |
193 | fmt.Println(err) | 200 | fmt.Println(err) |
194 | http.Redirect(w, r, "/name?label="+vars["label"] + "&error=" + gnunetError.Description, http.StatusSeeOther) | 201 | http.Redirect(w, r, "/name?label="+vars["label"] + "&error=" + gnunetError.Description, http.StatusSeeOther) |
195 | return | 202 | return |
196 | } | 203 | } |
197 | http.Redirect(w, r, "/name/"+ vars["label"] + "?registered=true", http.StatusSeeOther) | 204 | http.Redirect(w, r, "/name/"+ vars["label"] + "?registered=true", http.StatusSeeOther) |
198 | return | 205 | return |
199 | } | 206 | } |
200 | 207 | ||
201 | |||
202 | func (t *Registrar) searchPage(w http.ResponseWriter, r *http.Request) { | 208 | func (t *Registrar) searchPage(w http.ResponseWriter, r *http.Request) { |
203 | w.Header().Set("Content-Type", "text/html; charset=utf-8") | 209 | w.Header().Set("Content-Type", "text/html; charset=utf-8") |
204 | http.Redirect(w, r, "/name/"+r.URL.Query().Get("label"), http.StatusSeeOther) | 210 | http.Redirect(w, r, "/name/"+r.URL.Query().Get("label"), http.StatusSeeOther) |
205 | return | 211 | return |
206 | } | 212 | } |
207 | 213 | ||
214 | func (t *Registrar) expireRegistration(label string) (error) { | ||
215 | var gnunetError GnunetError | ||
216 | client := &http.Client{} | ||
217 | req, _ := http.NewRequest(http.MethodDelete,t.GnunetUrl+"/namestore/" + t.RootZoneName + "/" + label, nil) | ||
218 | resp, err := client.Do(req) | ||
219 | resp.Body.Close() | ||
220 | if err != nil { | ||
221 | return err | ||
222 | } | ||
223 | if http.StatusNotFound == resp.StatusCode { | ||
224 | return nil | ||
225 | } | ||
226 | if http.StatusNoContent != resp.StatusCode { | ||
227 | fmt.Printf("Got error: %d\n", resp.StatusCode) | ||
228 | err = json.NewDecoder(resp.Body).Decode(&gnunetError) | ||
229 | return errors.New("GNUnet REST API error: " + gnunetError.Description) | ||
230 | } | ||
231 | return nil | ||
232 | } | ||
233 | |||
234 | func (t *Registrar) setupRegistrationMetadataBeforePayment(label string, zkey string, orderId string, paymentUntil time.Time) (error) { | ||
235 | var namestoreRequest NamestoreRecord | ||
236 | var delegationRecord RecordData | ||
237 | var metadataRecord RecordData | ||
238 | var gnunetError GnunetError | ||
239 | var registrationMetadata RegistrationMetadata | ||
240 | delegationRecord.IsPrivate = true // Private until payment is through | ||
241 | delegationRecord.IsRelativeExpiration = true | ||
242 | delegationRecord.IsSupplemental = false | ||
243 | delegationRecord.IsMaintenance = false | ||
244 | delegationRecord.IsShadow = false | ||
245 | delegationRecord.RecordType = "PKEY" // FIXME get from value | ||
246 | delegationRecord.RelativeExpiration = uint64(t.RelativeDelegationExpiration.Microseconds()) | ||
247 | delegationRecord.Value = zkey | ||
248 | metadataRecord.IsPrivate = true | ||
249 | metadataRecord.IsRelativeExpiration = true | ||
250 | metadataRecord.IsSupplemental = false | ||
251 | metadataRecord.IsMaintenance = true | ||
252 | metadataRecord.IsShadow = false | ||
253 | metadataRecord.RecordType = "TXT" // FIXME use new recory type | ||
254 | metadataRecord.RelativeExpiration = uint64(t.RelativeDelegationExpiration.Microseconds()) | ||
255 | registrationMetadata = RegistrationMetadata{ | ||
256 | Paid: false, | ||
257 | OrderID: orderId, | ||
258 | NeedsPaymentUntil: paymentUntil, | ||
259 | Expiration: uint64(time.Now().Add(t.RelativeRegistrationExpiration).UnixMicro()), | ||
260 | } | ||
261 | metadataRecordValue, err := json.Marshal(registrationMetadata) | ||
262 | if nil != err { | ||
263 | return err | ||
264 | } | ||
265 | metadataRecord.Value = string(metadataRecordValue) | ||
266 | namestoreRequest.RecordName = label | ||
267 | namestoreRequest.Records = []RecordData{delegationRecord,metadataRecord} | ||
268 | reqString, _ := json.Marshal(namestoreRequest) | ||
269 | // FIXME handle errors here | ||
270 | fmt.Println(namestoreRequest) | ||
271 | resp, err := http.Post(t.GnunetUrl+"/namestore/" + t.RootZoneName, "application/json", bytes.NewBuffer(reqString)) | ||
272 | resp.Body.Close() | ||
273 | if http.StatusNoContent != resp.StatusCode { | ||
274 | fmt.Printf("Got error: %d\n", resp.StatusCode) | ||
275 | err = json.NewDecoder(resp.Body).Decode(&gnunetError) | ||
276 | return errors.New("GNUnet REST API error: " + gnunetError.Description) | ||
277 | } | ||
278 | return nil | ||
279 | } | ||
280 | |||
208 | func (t *Registrar) buyPage(w http.ResponseWriter, r *http.Request) { | 281 | func (t *Registrar) buyPage(w http.ResponseWriter, r *http.Request) { |
209 | vars := mux.Vars(r) | 282 | vars := mux.Vars(r) |
210 | if t.RegistrationCost == 0 { | 283 | /*if t.RegistrationCost == 0 { |
211 | http.Redirect(w, r, "/name/"+vars["label"]+"/register?zkey="+r.URL.Query().Get("zkey"), http.StatusSeeOther) | 284 | http.Redirect(w, r, "/name/"+vars["label"]+"/register?zkey="+r.URL.Query().Get("zkey"), http.StatusSeeOther) |
212 | return | 285 | return |
286 | }*/ | ||
287 | var metadataResponse RegistrationMetadata | ||
288 | var namestoreResponse NamestoreRecord | ||
289 | w.Header().Set("Content-Type", "text/html; charset=utf-8") | ||
290 | var metadataExists = false | ||
291 | // FIXME redirect back if label empty | ||
292 | resp, err := http.Get(t.GnunetUrl + "/namestore/" + t.RootZoneName + "/" + vars["label"] + "?include_maintenance=yes") | ||
293 | if err != nil { | ||
294 | fmt.Printf("Failed to get zone contents") | ||
295 | return | ||
296 | } | ||
297 | defer resp.Body.Close() | ||
298 | if http.StatusOK == resp.StatusCode { | ||
299 | respData, err := io.ReadAll(resp.Body) | ||
300 | if err != nil { | ||
301 | fmt.Printf("Failed to get zone contents" + err.Error()) | ||
302 | return | ||
303 | } | ||
304 | err = json.NewDecoder(bytes.NewReader(respData)).Decode(&namestoreResponse) | ||
305 | if err != nil { | ||
306 | fmt.Printf("Failed to get zone contents" + err.Error()) | ||
307 | return | ||
308 | } | ||
309 | for _, record := range namestoreResponse.Records { | ||
310 | if record.RecordType == "PKEY" { | ||
311 | continue | ||
312 | } | ||
313 | if record.RecordType == "TXT" { | ||
314 | err = json.Unmarshal([]byte(record.Value), &metadataResponse) | ||
315 | if err != nil { | ||
316 | fmt.Printf("Failed to get zone contents" + err.Error()) | ||
317 | return | ||
318 | } | ||
319 | metadataExists = true | ||
320 | } | ||
321 | } | ||
322 | } else { | ||
323 | fmt.Printf("Failed to get zone contents" + err.Error()) | ||
324 | return | ||
325 | } | ||
326 | var errorMsg = "" | ||
327 | // FIXME check for order expiration | ||
328 | if metadataExists { | ||
329 | if metadataResponse.Paid { | ||
330 | http.Redirect(w, r, "/name/" +vars["label"] + "?error=Registration failed: Already paid", http.StatusSeeOther) | ||
331 | return | ||
332 | } | ||
333 | if !time.Now().After(metadataResponse.NeedsPaymentUntil) { | ||
334 | http.Redirect(w, r, "/name/" + vars["label"] + "?error=Registration failed: Name is being paid", http.StatusSeeOther) | ||
335 | return | ||
336 | } | ||
337 | t.expireRegistration(vars["label"]) | ||
338 | } | ||
339 | orderID, newOrderErr := t.Merchant.AddNewOrder(*t.RegistrationCost, "GNS registrar name registration", "/name/" + vars["label"]) | ||
340 | if newOrderErr != nil { | ||
341 | fmt.Println(newOrderErr) | ||
342 | http.Redirect(w, r, "/name/"+vars["label"] + "?error=Registration failed: Unable to create order", http.StatusSeeOther) | ||
343 | return | ||
213 | } | 344 | } |
214 | http.Redirect(w, r, "/name/"+vars["label"]+"?error=Not implemented", http.StatusSeeOther) | 345 | metadataResponse.OrderID = orderID |
346 | |||
347 | payto, paytoErr := t.Merchant.IsOrderPaid(metadataResponse.OrderID) | ||
348 | if paytoErr != nil { | ||
349 | http.Redirect(w, r, "/name/"+vars["label"] + "?error=Registration failed: Error getting payment data", http.StatusSeeOther) | ||
350 | return | ||
351 | } | ||
352 | qrPng, qrErr := qrcode.Encode(payto, qrcode.Medium, 256) | ||
353 | if qrErr != nil { | ||
354 | http.Redirect(w, r, "/name/"+vars["label"] + "?error=Registration failed: Error generating QR code", http.StatusSeeOther) | ||
355 | return | ||
356 | } | ||
357 | paymentUntil := time.Now().Add(t.PaymentExpiration) | ||
358 | err = t.setupRegistrationMetadataBeforePayment(vars["label"], vars["zkey"], orderID, paymentUntil) | ||
359 | encodedPng := base64.StdEncoding.EncodeToString(qrPng) | ||
360 | fullData := map[string]interface{}{ | ||
361 | "qrCode": template.URL("data:image/png;base64," + encodedPng), | ||
362 | "payto": template.URL(payto), | ||
363 | "fulfillmentUrl": template.URL("/name/" + vars["label"]), | ||
364 | "label": vars["label"], | ||
365 | "error": errorMsg, | ||
366 | "cost": t.RegistrationCost, | ||
367 | "suffixHint": t.SuffixHint, | ||
368 | } | ||
369 | t.BuyTpl.Execute(w, fullData) | ||
215 | return | 370 | return |
216 | } | 371 | } |
217 | 372 | ||
@@ -264,7 +419,6 @@ func (t *Registrar) namePage(w http.ResponseWriter, r *http.Request) { | |||
264 | "label": vars["label"], | 419 | "label": vars["label"], |
265 | "error": r.URL.Query().Get("error"), | 420 | "error": r.URL.Query().Get("error"), |
266 | "cost": t.RegistrationCost, | 421 | "cost": t.RegistrationCost, |
267 | "currency": t.RegistrationCostCurrency, | ||
268 | "available": available, | 422 | "available": available, |
269 | "currentValue": value, | 423 | "currentValue": value, |
270 | "suffixHint": t.SuffixHint, | 424 | "suffixHint": t.SuffixHint, |
@@ -276,34 +430,6 @@ func (t *Registrar) namePage(w http.ResponseWriter, r *http.Request) { | |||
276 | return | 430 | return |
277 | } | 431 | } |
278 | 432 | ||
279 | func (t *Registrar) allNamesPage(w http.ResponseWriter, r *http.Request) { | ||
280 | var namestoreResponse []NamestoreRecord | ||
281 | w.Header().Set("Content-Type", "text/html; charset=utf-8") | ||
282 | resp, err := http.Get(t.GnunetUrl + "/namestore/" + t.RootZoneName) | ||
283 | if err != nil { | ||
284 | fmt.Printf("Failed to get zone contents") | ||
285 | return | ||
286 | } | ||
287 | defer resp.Body.Close() | ||
288 | if http.StatusOK != resp.StatusCode { | ||
289 | fmt.Printf("Failed to get zone contents. Retcode=%d", resp.StatusCode) | ||
290 | return | ||
291 | } | ||
292 | respData, err := io.ReadAll(resp.Body) | ||
293 | if err != nil { | ||
294 | fmt.Printf("Failed to get zone contents" + err.Error()) | ||
295 | return | ||
296 | } | ||
297 | err = json.NewDecoder(bytes.NewReader(respData)).Decode(&namestoreResponse) | ||
298 | if err != nil { | ||
299 | fmt.Printf("Failed to get zone contents" + err.Error()) | ||
300 | return | ||
301 | } | ||
302 | fmt.Println(namestoreResponse) | ||
303 | t.AllNamesTpl.Execute(w, namestoreResponse) | ||
304 | return | ||
305 | } | ||
306 | |||
307 | func (t *Registrar) setupHandlers() { | 433 | func (t *Registrar) setupHandlers() { |
308 | t.Router = mux.NewRouter().StrictSlash(true) | 434 | t.Router = mux.NewRouter().StrictSlash(true) |
309 | 435 | ||
@@ -312,7 +438,6 @@ func (t *Registrar) setupHandlers() { | |||
312 | t.Router.HandleFunc("/name/{label}/buy", t.buyPage).Methods("GET") | 438 | t.Router.HandleFunc("/name/{label}/buy", t.buyPage).Methods("GET") |
313 | t.Router.HandleFunc("/name/{label}/register", t.registerName).Methods("GET") | 439 | t.Router.HandleFunc("/name/{label}/register", t.registerName).Methods("GET") |
314 | t.Router.HandleFunc("/search", t.searchPage).Methods("GET") | 440 | t.Router.HandleFunc("/search", t.searchPage).Methods("GET") |
315 | t.Router.HandleFunc("/names", t.allNamesPage).Methods("GET") | ||
316 | 441 | ||
317 | /* ToS API */ | 442 | /* ToS API */ |
318 | // t.Router.HandleFunc("/terms", t.termsResponse).Methods("GET") | 443 | // t.Router.HandleFunc("/terms", t.termsResponse).Methods("GET") |
@@ -347,21 +472,21 @@ func (t *Registrar) Initialize(cfgfile string) { | |||
347 | if err != nil { | 472 | if err != nil { |
348 | fmt.Println(err) | 473 | fmt.Println(err) |
349 | } | 474 | } |
350 | allNamesTplFile := t.Cfg.Section("gns-registrar").Key("registrar_all_names").MustString("web/templates/names.html") | 475 | buyTplFile := t.Cfg.Section("gns-registrar").Key("buy_template").MustString("web/templates/buy.html") |
351 | t.AllNamesTpl, err = template.ParseFiles(allNamesTplFile) | 476 | t.BuyTpl, err = template.ParseFiles(buyTplFile) |
352 | if err != nil { | 477 | if err != nil { |
353 | fmt.Println(err) | 478 | fmt.Println(err) |
354 | } | 479 | } |
355 | t.RegistrationCost = t.Cfg.Section("gns-registrar").Key("registration_cost").MustUint64(0) | 480 | paymentExp := t.Cfg.Section("gns-registrar").Key("payment_required_expiration").MustString("48h") |
356 | recordExp := t.Cfg.Section("gns-registrar").Key("relative_delegation_expiration").MustString("24h") | 481 | recordExp := t.Cfg.Section("gns-registrar").Key("relative_delegation_expiration").MustString("24h") |
357 | registrationExp := t.Cfg.Section("gns-registrar").Key("registration_expiration").MustString("8760h") | 482 | registrationExp := t.Cfg.Section("gns-registrar").Key("registration_expiration").MustString("8760h") |
358 | fmt.Println(recordExp) | ||
359 | fmt.Println(registrationExp) | ||
360 | t.RelativeRegistrationExpiration, _ = time.ParseDuration(registrationExp) | 483 | t.RelativeRegistrationExpiration, _ = time.ParseDuration(registrationExp) |
361 | t.RelativeDelegationExpiration, _ = time.ParseDuration(recordExp) | 484 | t.RelativeDelegationExpiration, _ = time.ParseDuration(recordExp) |
485 | t.PaymentExpiration, _ = time.ParseDuration(paymentExp) | ||
362 | fmt.Println(t.RelativeDelegationExpiration) | 486 | fmt.Println(t.RelativeDelegationExpiration) |
363 | fmt.Println(t.RelativeRegistrationExpiration) | 487 | fmt.Println(t.RelativeRegistrationExpiration) |
364 | t.RegistrationCostCurrency = t.Cfg.Section("gns-registrar").Key("registration_cost_currency").MustString("EUR") | 488 | costStr := t.Cfg.Section("gns-registrar").Key("registration_cost").MustString("KUDOS:13") |
489 | t.RegistrationCost, err = talerutil.ParseAmount(costStr) | ||
365 | t.SuffixHint = t.Cfg.Section("gns-registrar").Key("suffix_hint").MustString("example.alt") | 490 | t.SuffixHint = t.Cfg.Section("gns-registrar").Key("suffix_hint").MustString("example.alt") |
366 | t.RootZoneName = t.Cfg.Section("gns-registrar").Key("root_zone_name").MustString("test") | 491 | t.RootZoneName = t.Cfg.Section("gns-registrar").Key("root_zone_name").MustString("test") |
367 | t.GnunetUrl = t.Cfg.Section("gns-registrar").Key("gnunet_baseurl_private").MustString("http://localhost:7776") | 492 | t.GnunetUrl = t.Cfg.Section("gns-registrar").Key("gnunet_baseurl_private").MustString("http://localhost:7776") |
@@ -390,8 +515,8 @@ func (t *Registrar) Initialize(cfgfile string) { | |||
390 | fmt.Printf("Failed to get zone contents" + err.Error()) | 515 | fmt.Printf("Failed to get zone contents" + err.Error()) |
391 | os.Exit(1) | 516 | os.Exit(1) |
392 | } | 517 | } |
393 | merchURL := t.Cfg.Section("gns-registrar").Key("merchant_baseurl_private").MustString("http://merchant.gnsregistrar/instances/myInstance") | 518 | merchURL := t.Cfg.Section("gns-registrar").Key("merchant_baseurl_private").MustString("https://backend.demo.taler.net") |
394 | merchToken := t.Cfg.Section("gns-registrar").Key("merchant_token").MustString("secretAccessToken") | 519 | merchToken := t.Cfg.Section("gns-registrar").Key("merchant_token").MustString("sandbox") |
395 | t.Merchant = merchant.NewMerchant(merchURL, merchToken) | 520 | t.Merchant = merchant.NewMerchant(merchURL, merchToken) |
396 | t.setupHandlers() | 521 | t.setupHandlers() |
397 | } | 522 | } |
diff --git a/web/templates/buy.html b/web/templates/buy.html new file mode 100644 index 0000000..e6d953b --- /dev/null +++ b/web/templates/buy.html | |||
@@ -0,0 +1,22 @@ | |||
1 | <!DOCTYPE html> | ||
2 | <html lang="en"> | ||
3 | <head> | ||
4 | <!-- Required meta tags --> | ||
5 | <meta charset="utf-8"> | ||
6 | <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | ||
7 | <link href="/css/bootstrap.min.css" rel="stylesheet"> | ||
8 | <title>Buy</title> | ||
9 | </head> | ||
10 | <body> | ||
11 | <div class ="container text-center"> | ||
12 | <h1 class="mb-3">To register your name <i class="text-primary">{{.label}}</i>.<i class="text-secondary">{{.suffixHint}}</i> please pay here:</h1> | ||
13 | |||
14 | <a href="{{.payto}}" class="btn btn-success mb-3">Pay with TALER</a><br/> | ||
15 | Alternatively, you can pay using your mobile wallet by scanning the QR code below:<br/> | ||
16 | <a href="{{.fulfillmentUrl}}"> | ||
17 | <img class="qr" src="{{.qrCode}}"/> | ||
18 | </a><br/> | ||
19 | After you pay with a mobile wallet, please click on the QR code to finalize your registration. | ||
20 | </div> | ||
21 | </body> | ||
22 | </html> | ||
diff --git a/web/templates/name.html b/web/templates/name.html index 2b780f4..5307668 100644 --- a/web/templates/name.html +++ b/web/templates/name.html | |||
@@ -31,14 +31,10 @@ | |||
31 | <h1 class="mb-5"><i class="text-primary">{{.label}}</i> is still <span class="text-success">available</span> for registration.</h1> | 31 | <h1 class="mb-5"><i class="text-primary">{{.label}}</i> is still <span class="text-success">available</span> for registration.</h1> |
32 | <form action="/name/{{.label}}/buy" method="get" class="align-items-center"> | 32 | <form action="/name/{{.label}}/buy" method="get" class="align-items-center"> |
33 | <div class="input-group mb-2 w-75"> | 33 | <div class="input-group mb-2 w-75"> |
34 | <span class="input-group-text" id="reg-prefix"><i class="text-primary">{{.label}}</i>.<i class="text-secondary">{{.suffixHint}}</i>: </span> | 34 | <span class="input-group-text" id="reg-prefix"><i class="text-primary">{{.label}}</i>.<i class="text-secondary">{{.suffixHint}}</i>: </span> |
35 | <input type="hidden" name="label" value="{{.label}}"> | 35 | <input type="hidden" name="label" value="{{.label}}"> |
36 | <input name="zkey" class="form-control form-control-lg" maxlength="63" type="text" placeholder="Enter your zone key here!" required autofocus> | 36 | <input name="zkey" class="form-control form-control-lg" maxlength="63" type="text" placeholder="Enter your zone key here!" required autofocus> |
37 | {{if eq .cost 0}} | 37 | <input class="btn btn-primary" type="submit" value="Register for {{.cost}}"> |
38 | <input class="btn btn-primary" type="submit" value="Register for free"> | ||
39 | {{else}} | ||
40 | <input class="btn btn-primary" type="submit" value="Register for {{.cost}} {{.currency}}"> | ||
41 | {{end}} | ||
42 | </div> | 38 | </div> |
43 | </form> | 39 | </form> |
44 | {{else}} | 40 | {{else}} |
@@ -54,10 +50,6 @@ | |||
54 | </form> | 50 | </form> |
55 | <form action="/renew" method="get" class="align-items-center"> | 51 | <form action="/renew" method="get" class="align-items-center"> |
56 | <span>Registration valid until: <i>{{.registeredUntil}}</i></span><br/> | 52 | <span>Registration valid until: <i>{{.registeredUntil}}</i></span><br/> |
57 | {{if .modificationAllowed}} | ||
58 | <span>Registration extension cost: <i>23 EUR</i></span><br/> | ||
59 | <input class="btn btn-primary" type="submit" value="Extend registration"> | ||
60 | {{end}} | ||
61 | </form> | 53 | </form> |
62 | {{end}} | 54 | {{end}} |
63 | <a class="btn btn-primary mt-5" href="/">Back</a> | 55 | <a class="btn btn-primary mt-5" href="/">Back</a> |
diff --git a/web/templates/names.html b/web/templates/names.html deleted file mode 100644 index f41ddf2..0000000 --- a/web/templates/names.html +++ /dev/null | |||
@@ -1,20 +0,0 @@ | |||
1 | <!DOCTYPE html> | ||
2 | <html lang="en"> | ||
3 | <head> | ||
4 | <!-- Required meta tags --> | ||
5 | <meta charset="utf-8"> | ||
6 | <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | ||
7 | <link href="/css/style.css" rel="stylesheet"> | ||
8 | <title>All Names Overview</title> | ||
9 | </head> | ||
10 | <body> | ||
11 | <div class="names"> | ||
12 | <!-- FIXME: Probably make this a table --> | ||
13 | <ul> | ||
14 | {{range $val := .}} | ||
15 | <li>{{$val.RecordName}}: {{(index $val.Records 0).Value}}</li> | ||
16 | {{end}} | ||
17 | </ul> | ||
18 | </div> | ||
19 | </body> | ||
20 | </html> | ||