3e0763463f
Add the vkpay package: verify a VK Mini Apps payment ("голоса") callback
signature — MD5 (mandated by VK's payment protocol) of the sig-excluded
parameters, sorted by name and concatenated key=value, with the app secret
appended; case-insensitive over the hex digest. Unit-tested (valid / tampered /
wrong-secret / missing). First piece of the VK rail; the two-phase callback
handler, the VK order branch and the client bridge follow.
41 lines
1.3 KiB
Go
41 lines
1.3 KiB
Go
// Package vkpay verifies VK Mini Apps payment ("голоса") callback signatures. VK signs each
|
|
// payment notification (get_item / order_status_change) and expects the receiving server to verify
|
|
// it with the app's secret before acting. The signature is MD5 — mandated by VK's payment protocol,
|
|
// not a security choice on our part — so this package uses crypto/md5 deliberately.
|
|
package vkpay
|
|
|
|
import (
|
|
"crypto/md5"
|
|
"encoding/hex"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// Verify reports whether params carry a valid VK payment signature under secret. VK computes the
|
|
// signature as the MD5 of the sig-excluded parameters, sorted alphabetically by name and
|
|
// concatenated as key=value with no separators, with the app secret appended. The comparison is
|
|
// case-insensitive over the hex digest.
|
|
func Verify(params map[string]string, secret string) bool {
|
|
got := params["sig"]
|
|
if got == "" || secret == "" {
|
|
return false
|
|
}
|
|
keys := make([]string, 0, len(params))
|
|
for k := range params {
|
|
if k == "sig" {
|
|
continue
|
|
}
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
var b strings.Builder
|
|
for _, k := range keys {
|
|
b.WriteString(k)
|
|
b.WriteString("=")
|
|
b.WriteString(params[k])
|
|
}
|
|
b.WriteString(secret)
|
|
sum := md5.Sum([]byte(b.String()))
|
|
return strings.EqualFold(hex.EncodeToString(sum[:]), got)
|
|
}
|