[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.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [taler-taldir] branch master updated: start merchant api integration. lots of open issues. tests fail.,
gnunet <=