gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-taldir] branch master updated: start merchant api integration. lo


From: gnunet
Subject: [taler-taldir] branch master updated: start merchant api integration. lots of open issues. tests fail.
Date: Mon, 11 Jul 2022 23:29:45 +0200

This is an automated email from the git hooks/post-receive script.

martin-schanzenbach pushed a commit to branch master
in repository taldir.

The following commit(s) were added to refs/heads/master by this push:
     new a147248  start merchant api integration. lots of open issues. tests 
fail.
a147248 is described below

commit a1472480495a815194c5dd55072637f937ac2877
Author: Martin Schanzenbach <schanzen@gnunet.org>
AuthorDate: Mon Jul 11 23:29:42 2022 +0200

    start merchant api integration. lots of open issues. tests fail.
---
 pkg/rest/taldir.go    |  46 +++++++++++----
 pkg/taler/merchant.go | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++
 pkg/util/helper.go    |  45 ++++++++++++++-
 3 files changed, 232 insertions(+), 13 deletions(-)

diff --git a/pkg/rest/taldir.go b/pkg/rest/taldir.go
index 941b228..0d8d18a 100644
--- a/pkg/rest/taldir.go
+++ b/pkg/rest/taldir.go
@@ -44,6 +44,7 @@ import (
   "encoding/base64"
   "taler.net/taldir/pkg/util"
   "taler.net/taldir/pkg/gana"
+  "taler.net/taldir/pkg/taler"
   "crypto/sha512"
   "gorm.io/driver/postgres"
   "gopkg.in/ini.v1"
@@ -87,6 +88,9 @@ type Taldir struct {
 
   // Challenge length in bytes before encoding
   ChallengeBytes int
+
+  // Merchant object
+  Merchant taler.Merchant
 }
 
 type VersionResponse struct {
@@ -207,6 +211,9 @@ type Validation struct {
 
   // The beginning of the last solution timeframe
   LastSolutionTimeframeStart time.Time
+
+  // The Taler Merchant Order ID
+  OrderId string
 }
 
 type ErrorDetail struct {
@@ -383,16 +390,6 @@ func (t *Taldir) registerRequest(w http.ResponseWriter, r 
*http.Request){
     w.Write(resp)
     return
   }
-  if util.AmountIsNonZero(t.Cfg.Section("taldir-" + 
vars["method"]).Key("challenge_fee").MustString("KUDOS:0")) ||
-     
util.AmountIsNonZero(t.Cfg.Section("taldir").Key("monthly_fee").MustString("KUDOS:0"))
 {
-    if len(order.Id) == 0 {
-      w.WriteHeader(http.StatusPaymentRequired)
-      return
-    }
-    // FIXME process order_id
-    w.WriteHeader(http.StatusNotImplemented)
-    return
-  }
   // Setup validation object. Retrieve object from DB if it already
   // exists.
   h := sha512.New()
@@ -420,6 +417,34 @@ func (t *Taldir) registerRequest(w http.ResponseWriter, r 
*http.Request){
   validation.PublicKey = req.PublicKey
   validation.SolutionAttemptCount = 0
   validation.LastSolutionTimeframeStart = time.Now()
+  amountSum, amountSumStr, _ := util.AmountSum(t.Cfg.Section("taldir-" + 
vars["method"]).Key("challenge_fee").MustString("KUDOS:0"),
+                                               
t.Cfg.Section("taldir").Key("monthly_fee").MustString("KUDOS:0"))
+  if amountSum > 0 {
+    // FIXME what if provided order ID and validation order ID differ???
+    if len(validation.OrderId) == 0 {
+      // Add new order for new validations
+      orderId, newOrderErr := t.Merchant.AddNewOrder(amountSumStr)
+      if newOrderErr != nil {
+        w.WriteHeader(http.StatusInternalServerError)
+        return
+      }
+      validation.OrderId = orderId
+    }
+    // Check if order paid
+    payto, paytoErr := t.Merchant.IsOrderPaid(validation.OrderId)
+    if paytoErr != nil {
+      if len(payto) != 0 {
+        w.WriteHeader(http.StatusPaymentRequired)
+        w.Header().Set("Location", payto) // FIXME no idea what to do with 
this.
+        return
+      }
+      w.WriteHeader(http.StatusInternalServerError)
+      log.Println(paytoErr)
+      return
+    }
+    // In this case, this order was paid
+  }
+
   if err == nil {
     // Limit re-initiation attempts
     validation.InitiationCount++
@@ -733,6 +758,7 @@ func (t *Taldir) Initialize(cfgfile string) {
   if "" == t.Salt {
     t.Salt = t.Cfg.Section("taldir").Key("salt").MustString("ChangeMe")
   }
+  t.Merchant = taler.NewMerchant("http://localhost:8880";, "myInstance")
   t.setupHandlers()
 }
 
diff --git a/pkg/taler/merchant.go b/pkg/taler/merchant.go
new file mode 100644
index 0000000..3c039e4
--- /dev/null
+++ b/pkg/taler/merchant.go
@@ -0,0 +1,154 @@
+package taler
+
+import (
+  "net/http"
+  "encoding/json"
+  "bytes"
+  "fmt"
+  "errors"
+)
+
+type PostOrderRequest struct {
+  // The order must at least contain the minimal
+  // order detail, but can override all.
+  order MinimalOrderDetail
+
+  // If set, the backend will then set the refund deadline to the current
+  // time plus the specified delay.  If it's not set, refunds will not be
+  // possible.
+  RefundDelay int64 `json:"refund_delay,omitempty"`
+
+  // Specifies the payment target preferred by the client. Can be used
+  // to select among the various (active) wire methods supported by the 
instance.
+  PaymentTarget string `json:"payment_target,omitempty"`
+
+  // Specifies that some products are to be included in the
+  // order from the inventory.  For these inventory management
+  // is performed (so the products must be in stock) and
+  // details are completed from the product data of the backend.
+  // FIXME: Not sure we actually need this for now
+  //InventoryProducts []MinimalInventoryProduct 
`json:"inventory_products,omitempty"`
+
+  // Specifies a lock identifier that was used to
+  // lock a product in the inventory.  Only useful if
+  // inventory_products is set.  Used in case a frontend
+  // reserved quantities of the individual products while
+  // the shopping cart was being built.  Multiple UUIDs can
+  // be used in case different UUIDs were used for different
+  // products (i.e. in case the user started with multiple
+  // shopping sessions that were combined during checkout).
+  LockUuids []string `json:"lock_uuids"`
+
+  // Should a token for claiming the order be generated?
+  // False can make sense if the ORDER_ID is sufficiently
+  // high entropy to prevent adversarial claims (like it is
+  // if the backend auto-generates one). Default is 'true'.
+  CreateToken bool `json:"create_token,omitempty"`
+
+}
+
+type MinimalOrderDetail struct {
+  // Amount to be paid by the customer.
+  Amount string
+
+  // Short summary of the order.
+  Summary string;
+}
+
+
+// NOTE: Part of the above but optional
+type FulfillmentMetadata struct {
+  // See documentation of fulfillment_url in ContractTerms.
+  // Either fulfillment_url or fulfillment_message must be specified.
+  FulfillmentUrl string `json:"fulfillment_url,omitempty"`
+
+  // See documentation of fulfillment_message in ContractTerms.
+  // Either fulfillment_url or fulfillment_message must be specified.
+  FulfillmentMessage string `json:"fulfillment_message,omitempty"`
+}
+
+type PostOrderResponse struct {
+  // Order ID of the response that was just created.
+  OrderId string `json:"order_id"`
+}
+
+type PostOrderResponseToken struct {
+  // Token that authorizes the wallet to claim the order.
+  // Provided only if "create_token" was set to 'true'
+  // in the request.
+  Token string
+}
+
+type CheckPaymentStatusResponse struct {
+  // Status of the order
+  OrderStatus string `json:"order_status"`
+}
+
+type CheckPaymentPaytoResponse struct {
+  // Status of the order
+  TalerPayUri string `json:"taler_pay_uri"`
+}
+
+
+
+type Merchant struct {
+
+  // The host of this merchant
+  Host string;
+
+  // The instance of this merchant
+  Instance string;
+}
+
+func NewMerchant(merchHost string, merchInstance string) Merchant {
+  return Merchant{
+    Host: merchHost,
+    Instance: merchInstance,
+  }
+}
+
+func (m *Merchant) IsOrderPaid(orderId string) (string, error) {
+  var orderPaidResponse CheckPaymentStatusResponse
+  var paytoResponse CheckPaymentPaytoResponse
+  resp, err := http.Get(m.Host + "/instances/"+ m.Instance + 
"/private/orders/" + orderId)
+  if nil != err {
+    return "", err
+  }
+  defer resp.Body.Close()
+  if http.StatusOK != resp.StatusCode {
+    message := fmt.Sprintf("Expected response code %d. Got %d", http.StatusOK, 
resp.StatusCode)
+    return "", errors.New(message)
+  }
+  err = json.NewDecoder(resp.Body).Decode(&orderPaidResponse)
+  if err != nil {
+    return "", err
+  }
+  if orderPaidResponse.OrderStatus != "paid" {
+    err = json.NewDecoder(resp.Body).Decode(&paytoResponse)
+    return paytoResponse.TalerPayUri, err
+  }
+  return "", nil
+}
+
+func (m *Merchant) AddNewOrder(amount string) (string, error) {
+  var newOrder PostOrderRequest
+  var orderDetail MinimalOrderDetail
+  var orderResponse PostOrderResponse
+  orderDetail.Amount = amount
+  // FIXME get from cfg
+  orderDetail.Summary = "This is an order to a TalDir registration"
+  newOrder.order = orderDetail
+  reqString, _ := json.Marshal(newOrder)
+  resp, err := http.Post(m.Host + "/instances/"+ m.Instance + 
"/private/orders", "application/json", bytes.NewBuffer(reqString))
+
+  if nil != err {
+    return "", err
+  }
+  defer resp.Body.Close()
+  if http.StatusOK != resp.StatusCode {
+    message := fmt.Sprintf("Expected response code %d. Got %d", http.StatusOK, 
resp.StatusCode)
+    return "", errors.New(message)
+  }
+  err = json.NewDecoder(resp.Body).Decode(&orderResponse)
+  return orderResponse.OrderId, err
+}
diff --git a/pkg/util/helper.go b/pkg/util/helper.go
index 2056a29..62625dd 100644
--- a/pkg/util/helper.go
+++ b/pkg/util/helper.go
@@ -23,6 +23,7 @@ import (
   "fmt"
   "crypto/sha512"
   "math/rand"
+  "errors"
   "strings"
   "strconv"
 )
@@ -53,13 +54,51 @@ func GenerateChallenge(bytes int) string {
 
 // Check if this is a non-zero, positive amount
 func AmountIsNonZero(amount string) bool {
+  amountFloat, _ := AmountToFloat(amount)
+  return amountFloat > 0
+}
+
+func AmountCurrency(amount string) (string, error) {
+  s := strings.Split(amount, ":")
+  if len(s) != 2 {
+    return "", errors.New("Malformed amount")
+  }
+  return s[0], nil
+}
+
+
+func AmountToFloat(amount string) (float64, error) {
   s := strings.Split(amount, ":")
   if len(s) != 2 {
-    return false
+    return 0.0, errors.New("Malformed amount")
   }
   amountFloat, err := strconv.ParseFloat(s[1], 64)
   if err != nil {
-    return false
+    return 0.0, errors.New("Malformed value in amount")
   }
-  return amountFloat > 0
+  return amountFloat, nil
+}
+
+// Check if this is a non-zero, positive amount
+func AmountSum(amountA string, amountB string) (float64, string, error) {
+  curA, err := AmountCurrency(amountA)
+  if nil != err {
+    return 0.0, "", errors.New("Currency in amount malformed")
+  }
+  curB, err := AmountCurrency(amountB)
+  if nil != err {
+    return 0.0, "", errors.New("Currency in amount malformed")
+  }
+  if curA != curB {
+    return 0.0, "", errors.New("Currency in amounts different")
+  }
+  valA, err := AmountToFloat(amountA)
+  if err != nil {
+    return 0.0, "", err
+  }
+  valB, err := AmountToFloat(amountB)
+  if err != nil {
+    return 0.0, "", err
+  }
+  return valA + valB, curA, nil
 }

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]