paytrail / api-documentation

Paytrail Payment API documentation
MIT License
6 stars 11 forks source link

HMAC is not what it should be #97

Closed MiesSuomesta closed 11 months ago

MiesSuomesta commented 11 months ago

Describe the bug

The bug is that the SHA provided is not got when running the example?

Steps to Reproduce Run following code:

package main
import( 
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"sort"
"fmt"
)

var PaytrailOfficialRequestSignature = "3708f6497ae7cc55a2e6009fc90aa10c3ad0ef125260ee91b19168750f6d74f6"

func headersToBytesSorted(headers map[string]string) (sortedHeaders []byte) {
    var keys []string
    for key := range headers {
            if key[:9] == "checkout-" {
                keys = append(keys, key)
            }
    }
    sort.Strings(keys)
    for _, key := range keys {
                    sortedHeaders = append(sortedHeaders, []byte(key+":"+headers[key]+"\n")...)
    }
    return sortedHeaders
}

func CalculateHmac(secret, body []byte, headers map[string]string) string {

    payload := headersToBytesSorted(headers)
    payload = append(payload, body...)
    hash := hmac.New(sha256.New, secret)
    hash.Write(payload)
    return hex.EncodeToString(hash.Sum(nil))
}

var PaytrailOfficialRequestHeaders = map[string]string{
    "checkout-account":   "375917",
    "checkout-algorithm": "sha256",
    "checkout-method":    "POST",
    "checkout-nonce":     "564635208570151",
    "checkout-timestamp": "2018-07-06T10:01:31.904Z",
}

var PaytrailOfficialRequestBody = []byte(`{
    "stamp": "unique-identifier-for-merchant",
    "reference": "3759170",
    "amount": 1525,
    "currency": "EUR",
    "language": "FI",
    "items": [
      {
        "unitPrice": 1525,
        "units": 1,
        "vatPercentage": 24,
        "productCode": "#1234",
        "deliveryDate": "2018-09-01"
      }
    ],
    "customer": {
      "email": ["test.customer@example.com"]
    },
    "redirectUrls": {
      "success": ["https://ecom.example.com/cart/success"],
      "cancel": ["https://ecom.example.com/cart/cancel"]
    }
  }`)

func main() {
    signatureStrOut := CalculateHmac([]byte("SAIPPUAKAUPPIAS"),  PaytrailOfficialRequestBody, PaytrailOfficialRequestHeaders)
    fmt.Println("Const :", PaytrailOfficialRequestSignature)
    fmt.Println("Got   :", signatureStrOut)
}

Expected behaviour

Output should be the hash from the example:: 3708f6497ae7cc55a2e6009fc90aa10c3ad0ef125260ee91b19168750f6d74f6 but what I got was : c7340510147ab5fe988fcf27f755b374f370aba2c612484f0fe3878936f961d6

Actual behaviour

Somehow it calculates wrong...

MiesSuomesta commented 11 months ago

hmm the formatting mailto and links are not in my code then :D EDIT: haa i got it nice :)

MiesSuomesta commented 11 months ago

Now i got it runnning :D sorry for trouble :D

MiesSuomesta commented 11 months ago

The problem was to use compact & buffer :D Thanks!

Working code:

package main
import( 
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"sort"
"fmt"
)

var PaytrailOfficialRequestSignature = "3708f6497ae7cc55a2e6009fc90aa10c3ad0ef125260ee91b19168750f6d74f6"

func headersToBytesSorted(headers map[string]string) (sortedHeaders []byte) {
    var keys []string
    for key := range headers {
        if key[:9] == "checkout-" {
            keys = append(keys, key)
        }
    }
    sort.Strings(keys)
    for _, key := range keys {
                    sortedHeaders = append(sortedHeaders, []byte(key+":"+headers[key]+"\n")...)
    }
    return sortedHeaders
}

func CalculateHmac(secret, body []byte, headers map[string]string) string {

    payload := headersToBytesSorted(headers)
    payload = append(payload, body...)
    hash := hmac.New(sha256.New, secret)
    hash.Write(payload)
    return hex.EncodeToString(hash.Sum(nil))
}

var PaytrailOfficialRequestHeaders = map[string]string{
    "checkout-account":   "375917",
    "checkout-algorithm": "sha256",
    "checkout-method":    "POST",
    "checkout-nonce":     "564635208570151",
    "checkout-timestamp": "2018-07-06T10:01:31.904Z",
}

var PaytrailOfficialRequestBody = []byte(`{
    "stamp": "unique-identifier-for-merchant",
    "reference": "3759170",
    "amount": 1525,
    "currency": "EUR",
    "language": "FI",
    "items": [
      {
        "unitPrice": 1525,
        "units": 1,
        "vatPercentage": 24,
        "productCode": "#1234",
        "deliveryDate": "2018-09-01"
      }
    ],
    "customer": {
      "email": "test.customer@example.com"
    },
    "redirectUrls": {
      "success": "https://ecom.example.com/cart/success",
      "cancel": "https://ecom.example.com/cart/cancel"
    }
  }`)

func main() {
    buf := new(bytes.Buffer)
    json.Compact(buf, PaytrailOfficialRequestBody)
    signatureStrOut := CalculateHmac([]byte("SAIPPUAKAUPPIAS"),  buf.Bytes(), PaytrailOfficialRequestHeaders)
    fmt.Println("Const :", PaytrailOfficialRequestSignature)
    fmt.Println("Got   :", signatureStrOut)
}