cashless2ecash

cashless2ecash: pay with cards for digital cash (experimental)
Log | Files | Refs | README

commit f5ef8a38f8d87b3f301e5ecc5500060c71cb1bf0
parent 7f7d5959ad8b6c18533a2cbe0747a86709c69cfa
Author: Joel-Haeberli <haebu@rubigen.ch>
Date:   Mon,  3 Jun 2024 12:22:26 +0200

fix: amount bug

Diffstat:
Mc2ec/amount.go | 16++++------------
Mc2ec/amount_test.go | 39++++++++++++++++++++++++++++++++++-----
Mc2ec/api-wire-gateway.go | 2+-
Mc2ec/codec_test.go | 19+++++++++++++++++++
Mc2ec/encoding.go | 2+-
Mc2ec/payto.go | 7+++----
Mc2ec/proc-transfer.go | 4++--
Mc2ec/wallee-client.go | 2+-
8 files changed, 65 insertions(+), 26 deletions(-)

diff --git a/c2ec/amount.go b/c2ec/amount.go @@ -119,7 +119,7 @@ func toFractionStr(frac int, fractionalDigits int) string { // Subtract the amount b from a and return the result. // a and b must be of the same currency and a >= b -func (a *Amount) Sub(b Amount) (*Amount, error) { +func (a *Amount) Sub(b Amount, fractionalDigits int) (*Amount, error) { if a.Currency != b.Currency { return nil, errors.New("currency mismatch") } @@ -127,7 +127,7 @@ func (a *Amount) Sub(b Amount) (*Amount, error) { f := a.Fraction if a.Fraction < b.Fraction { v -= 1 - f += FractionalBase + f += uint64(math.Pow10(fractionalDigits)) } f -= b.Fraction if v < b.Value { @@ -145,7 +145,7 @@ func (a *Amount) Sub(b Amount) (*Amount, error) { // Add b to a and return the result. // Returns an error if the currencies do not match or the addition would // cause an overflow of the value -func (a *Amount) Add(b Amount) (*Amount, error) { +func (a *Amount) Add(b Amount, fractionalDigits int) (*Amount, error) { if a.Currency != b.Currency { return nil, errors.New("currency mismatch") } @@ -156,7 +156,7 @@ func (a *Amount) Add(b Amount) (*Amount, error) { if v >= MaxAmountValue { return nil, fmt.Errorf("amount overflow (%d > %d)", v, MaxAmountValue) } - f := uint64((a.Fraction + b.Fraction) % FractionalBase) + f := uint64((a.Fraction + b.Fraction) % uint64(math.Pow10(fractionalDigits))) r := Amount{ Currency: a.Currency, Value: v, @@ -223,12 +223,4 @@ func (a *Amount) IsZero() bool { func (a *Amount) String(fractionalDigits int) string { return FormatAmount(a, fractionalDigits) - - // v := strconv.FormatUint(a.Value, 10) - // if a.Fraction != 0 { - // f := strconv.FormatUint(a.Fraction, 10) - // f = strings.TrimRight(f, "0") - // v = fmt.Sprintf("%s.%s", v, f) - // } - // return fmt.Sprintf("%s:%s", a.Currency, v) } diff --git a/c2ec/amount_test.go b/c2ec/amount_test.go @@ -1,5 +1,6 @@ // This file is part of taler-go, the Taler Go implementation. // Copyright (C) 2022 Martin Schanzenbach +// Copyright (C) 2024 Joel Häberli // // Taler Go is free software: you can redistribute it and/or modify it // under the terms of the GNU Affero General Public License as published @@ -40,7 +41,7 @@ var c = Amount{ } func TestAmountAdd(t *testing.T) { - d, err := a.Add(b) + d, err := a.Add(b, 8) if err != nil { t.Errorf("Failed adding amount") } @@ -50,7 +51,7 @@ func TestAmountAdd(t *testing.T) { } func TestAmountSub(t *testing.T) { - d, err := c.Sub(b) + d, err := c.Sub(b, 8) if err != nil { t.Errorf("Failed substracting amount") } @@ -65,7 +66,7 @@ func TestAmountLarge(t *testing.T) { fmt.Println(err) t.Errorf("Failed") } - _, err = x.Add(a) + _, err = x.Add(a, 2) if err != nil { fmt.Println(err) t.Errorf("Failed") @@ -93,7 +94,7 @@ func TestAmountSub2(t *testing.T) { t.FailNow() } fmt.Println("subtracting", am.String(2)) - a2, err := am.Sub(*am) + a2, err := am.Sub(*am, 2) if err != nil { fmt.Println("subtracting failed!", a, err) t.FailNow() @@ -122,7 +123,7 @@ func TestAmountSub3(t *testing.T) { t.FailNow() } fmt.Println("subtracting", am.String(4)) - a2, err := am.Sub(*am) + a2, err := am.Sub(*am, 4) if err != nil { fmt.Println("subtracting failed!", a, err) t.FailNow() @@ -268,3 +269,31 @@ func TestFormatAmountInvalid(t *testing.T) { } } } + +func TestFeesSub(t *testing.T) { + + amountWithFeesStr := fmt.Sprintf("%s:%s", "CHF", "5.00") + amountWithFees, err := ParseAmount(amountWithFeesStr, 3) + if err != nil { + fmt.Println("failed!", err) + t.FailNow() + } + + fees, err := ParseAmount("CHF:0.005", 3) + if err != nil { + fmt.Println("failed!", err) + t.FailNow() + } + + refundAmount, err := amountWithFees.Sub(*fees, 3) + if err != nil { + fmt.Println("failed!", err) + t.FailNow() + } + + if amnt := refundAmount.String(3); amnt != "CHF:4.995" { + fmt.Println("expected the refund amount to be CHF:4.995, but it was", amnt) + } else { + fmt.Println("refundable amount:", amnt) + } +} diff --git a/c2ec/api-wire-gateway.go b/c2ec/api-wire-gateway.go @@ -173,7 +173,7 @@ func transfer(res http.ResponseWriter, req *http.Request) { return } - paytoTargetType, tid, err := ParsePaytoWalleeTransaction(transfer.CreditAccount) + paytoTargetType, tid, err := ParsePaytoUri(transfer.CreditAccount) if err != nil { LogError("wire-gateway-api", err) setLastResponseCodeForLogger(HTTP_BAD_REQUEST) diff --git a/c2ec/codec_test.go b/c2ec/codec_test.go @@ -60,3 +60,22 @@ func TestJsonCodecRoundTrip(t *testing.T) { assert.DeepEqual(t, &testObj, decodedTestObj) } + +func TestTransferRequest(t *testing.T) { + + reqStr := "{\"request_uid\":\"test-1\",\"amount\":\"CHF:4.95\",\"exchange_base_url\":\"https://exchange.chf.taler.net\",\"wtid\":\"\",\"credit_account\":\"payto://wallee-transaction/R361ZT45TZ026EQ0S909C88F0E2YJY11HXV0VQTCHKR2VHA7DQCG\"}" + + fmt.Println("request string:", reqStr) + + codec := NewJsonCodec[TransferRequest]() + + rdr := bytes.NewReader([]byte(reqStr)) + + req, err := codec.Decode(rdr) + if err != nil { + fmt.Println("error:", err) + t.FailNow() + } + + fmt.Println(req) +} diff --git a/c2ec/encoding.go b/c2ec/encoding.go @@ -72,7 +72,7 @@ func decodeCrock(e string) ([]byte, error) { return i, nil } - return -1, errors.New("encoding error") + return -1, errors.New("crockford decoding error") } for readPosition < size || bitpos > 0 { diff --git a/c2ec/payto.go b/c2ec/payto.go @@ -33,9 +33,8 @@ func ParsePaytoWalleeTransaction(uri string) (string, string, error) { if t, i, err := ParsePaytoUri(uri); err != nil { - _, err := decodeCrock(i) - if err != nil { - return "", "", errors.New("invalid transaction-id for wallee-transaction") + if t != "wallee-transaction" { + return "", "", errors.New("expected payto target type 'wallee-transaction'") } return t, i, nil @@ -53,7 +52,7 @@ func ParsePaytoUri(uri string) (string, string, error) { parts := strings.Split(raw, PAYTO_PARTS_SEPARATOR) if len(parts) < 2 { - return "", "", errors.New("invalid wallee-transaction payto-uri") + return "", "", errors.New("invalid payto-uri") } return parts[0], parts[1], nil diff --git a/c2ec/proc-transfer.go b/c2ec/proc-transfer.go @@ -64,7 +64,7 @@ func transferCallback(notification *Notification, errs chan error) { errs <- err } - paytoTargetType, tid, err := ParsePaytoWalleeTransaction(transfer.CreditAccount) + paytoTargetType, tid, err := ParsePaytoUri(transfer.CreditAccount) if err != nil { errs <- errors.New("malformed transfer request uid: " + err.Error()) transferFailed(transfer, errs) @@ -118,7 +118,7 @@ func executePendingTransfers(errs chan error) { continue } - paytoTargetType, tid, err := ParsePaytoWalleeTransaction(t.CreditAccount) + paytoTargetType, tid, err := ParsePaytoUri(t.CreditAccount) if err != nil { LogError("proc-transfer", err) errs <- err diff --git a/c2ec/wallee-client.go b/c2ec/wallee-client.go @@ -205,7 +205,7 @@ func (w *WalleeClient) Refund(transactionId string) error { return err } - refundAmount, err := amountWithFees.Sub(*fees) + refundAmount, err := amountWithFees.Sub(*fees, CONFIG.Server.CurrencyFractionDigits) if err != nil { LogError("wallee-client", err) return err