Signature Generation
SNAP API
Signature Generation
The signature is used to authenticate requests sent to the SNAP API. Winpay uses the Asymmetric Without Get Token (RSA-SHA256) security method so merchants do not need to request additional get tokens.
Private Key & Public Key
Before a merchant can generate a signature, it must have an RSA private key and a public key. To get the private key and public key, merchants can generate their own or use private keys and public keys from third-party sites such as: https://cryptotools.net/rsagen.
Formula Signature
Signatures are generated from data sent to the Snap API. Here is the formula for generating a signature:
stringToSign = HTTPMethod +":"+ EndpointUrl +":"+ Lowercase(HexEncode(SHA-256(minify(RequestBody)))) + ":" + TimeStamp
signature = base64_encode(SHA256withRSA(private_key, stringToSign))
Source Code Signature Creation
- Go
- PHP
- Node JS
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"strconv"
"strings"
"time"
)
var privateKey = []byte(`
MIIEowIBAAKCAQB5jAWUddv1tZW6g4shKbc7gU51rHSZZsTlJkNkxVGr2y4uc7kU
MT4Rnt+RpHcRoQ/cQzen8D3yRmBgOJ6OX+lJanKjlUolsAx9RTolvE1jTSL71GA7
MzBvgcH4cIaLIPAk+JdT6y+upVblY8WnjUbqsw+IJ8J0jru71zC23xaAbDARAmyz
q+3ehfia5LT/jXYA6aCxLb/O+LZRU35/midQTw7iTqsAW5qlioRX/Ws1GdgqaaS0
Ztzi6E0gdViDQFwtxOxmJ5eNiqPTRp63odqeymMT3i5KzzyRQct3EJU8RRbwfX9Z
JQw0crHOqTBE2LHDyOL+9Xsuk5g6CPY5Ent/AgMBAAECggEAWpyZOFEY1rc1VSNl
oCZyNAk0zaLwFcA4fsAks8YqGZ0/c1/QFQ6UwBwZfYEHRvw0T7dIkYkXFTuD85ei
FG/KAXI63lqYOTpiRS1LqQAVDTdfIi/0QY3qyOYKDcUNeEDo5hbw8Z2+IUufWUkk
+8Dlomg05wMFA1G5ANLuvRXYt4lkdxs4hUjrYS5GlZ1t5qtAdLOfmxE3gaYhyV86
4Jeocf0BHDkH9HupIHwPIxxBizFSZOgiKKvLosQf4yv0WNU7dAAJVuF2+KVxNsy+
QMNU77rpuqn0cxKOjRwup2tKCZEsucDIE9YAJrPLeAH8zNq0v5JA96IBAIrFcMpF
PfV+gQKBgQDTnpvLzK1dUNSCoQl5Q3RWIK8XAank93BlQk7akLfJB2CkC8YXefJR
xKAhWxIY65DL1oxklPEmBZXHb+RhUG8IxiH8uqleSd/LqM31Bum/FSb98okSK7JN
h5D1Xl89uk5kz3wP5d4xREsLXejYmOCVrrebjSauE8Su6ip17giyPwKBgQCTCZy1
083TcwJBF2n2WAR7/yn2eb7PU1m03QZ5FUinZGVZcNV2wYX0HSawJJJjcJ/0Ekp2
gJsJIaPPC4ug+Ul+Upn2w9aOg9Y4VJFmSyieI41paHgp1CMPnt0OWlOYMYrZXrpC
URuVzlUWsXyHvLJ4R7BjMJah9R6j1bSFFMFmwQKBgQCu2qQyfflVXH9rPDJ+vwy2
SaVCpj5CEW4OTP6ou+EPuwfQJGZWaY2eoJALX5uRebsXojw4s5SZ7Q14T+ztt683
GbdlgrqFTRccyWr8Je0n1Qt101wM5owaYYSWKZcagwFZbNM+s5mnLZ2wU7Ucmx0e
sNHHNpcaSP0/5f6VmMwYswKBgQCF407LUt4tGKCI3OUj5+nNbv+q7BkPeHpK8qge
QX8yzXxsAQ6fYIC1VIYSBns2CSvdd3pOhbSmj/c1ZPHz1otRd62ywYjxpKLAEGGF
oBKQTRdLUFF65Sw34RSxKRbNku84F8XK9UgFFSjtzRf0EaMBW2YGLAw91ZHc5Y/H
Tqr9AQKBgC884u+LkjR1e2osGwwWZlfJQsT47cC0EaFrFGuoMv5ND4Gya4dCsPLr
gzPjd9XK/qRWpiPoHeOS959sV43yXVMU/LX2WnIrJbQbCc+UYGN5E0uQt5hkpXta
+yourPrivateKey
`)
func generateSignature(method string, path string, body string, tStamp string) string {
pkBytes, err := base64.StdEncoding.DecodeString(string(privateKey))
if err != nil {
panic(err)
}
privKey, err := x509.ParsePKCS1PrivateKey(pkBytes)
if err != nil {
panic(err)
}
xbody, _ := Minify([]byte(body))
stringToSign := fmt.Sprintf("%s:%s:%s:%s", method, path, string(xbody), tStamp)
fmt.Println("stringToSign: ", stringToSign)
hashed := sha256.Sum256([]byte(stringToSign))
signature, err := rsa.SignPKCS1v15(rand.Reader, privKey, crypto.SHA256, hashed[:])
if err != nil {
panic(err)
}
return base64.StdEncoding.EncodeToString(signature)
}
func Minify(body []byte) ([]byte, error) {
buff := new(bytes.Buffer)
errCompact := json.Compact(buff, body)
if errCompact != nil {
newErr := fmt.Errorf("failure encountered compacting json := %v", errCompact)
return nil, newErr
}
b, err := io.ReadAll(buff)
if err != nil {
readErr := fmt.Errorf("read buffer error encountered := %v", err)
return nil, readErr
}
// LowerCase(HexEncode(SHA-256(stringToMinify)))
hasher := sha256.New()
hasher.Write([]byte(b))
hash := hex.EncodeToString(hasher.Sum(nil))
return []byte(strings.ToLower(hash)), nil
}
type Amount struct {
Value string `json:"value"`
Currency string `json:"currency"`
}
type AdditionalInfo struct {
Channel string `json:"channel"`
}
type CreateVaPayload struct {
CustomerNo string `json:"customerNo"`
VirtualAccountName string `json:"virtualAccountName"`
TrxId string `json:"trxId"`
TotalAmount Amount `json:"totalAmount"`
VirtualAccountTrxType string `json:"virtualAccountTrxType"`
ExpiredDate string `json:"expiredDate"`
AdditionalInfo AdditionalInfo `json:"additionalInfo"`
}
func main() {
xPath := "/v1.0/transfer-va/create-va"
xMethod := "POST"
tStamp := "2023-09-19T12:11:14+07:00"
xPartnerId := "962489e9-de5d-4eb7-92a4-b07d44d64bf4"
payload := CreateVaPayload{
CustomerNo: "1234567891237",
VirtualAccountName: "CHUS PANDI",
TrxId: "invoice-00000000000000003",
TotalAmount: Amount{
Value: "15000.00",
Currency: "IDR",
},
VirtualAccountTrxType: "c",
ExpiredDate: time.Now().Add(time.Hour).Format("2006-01-02T15:04:05+07:00"),
AdditionalInfo: AdditionalInfo{
Channel: "BSI",
},
}
xBody, _ := json.Marshal(payload)
signature := generateSignature(xMethod, xPath, string(xBody), tStamp)
log.Println("Your Signature: ",signature)
}
$httpMethod = 'POST';
$endpointUrl = '/v1.0/transfer-va/create-va';
$timestamp = '2023-09-19T12:11:14+07:00';
$payload = '
{
"customerNo": "08123456789",
"virtualAccountName": "CHUS PANDI",
"trxId": "INV-000000001",
"totalAmount": {
"value": "10000.00",
"currency": "IDR"
},
"virtualAccountTrxType": "c",
"expiredDate": "2023-11-02T17:18:48+07:00",
"additionalInfo": {
"channel": "BSI"
}
}
';
$body = json_decode($payload);
$hashedBody = strtolower(bin2hex(hash('sha256', json_encode($body, JSON_UNESCAPED_SLASHES), true)));
$stringToSign = [
$httpMethod,
$endpointUrl,
$hashedBody,
$timestamp
];
$signature = '';
$stringToSign = implode(':', $stringToSign);
$privKey = openssl_get_privatekey('./your-private-key-file.pem');
openssl_sign($stringToSign, $signature, $privKey, OPENSSL_ALGO_SHA256);
$encodedSignature = base64_encode($signature);
echo 'Your Signature:' $encodedSignature.
const httpMethod = 'POST';
const endpointUrl = '/v1.0/transfer-va/create-va';
const timestamp = '2023-09-19T12:11:14+07:00';
const payload = `
{
"customerNo": "08123456789",
"virtualAccountName": "CHUS PANDI",
"trxId": "INV-000000001",
"totalAmount": {
"value": "10000.00",
"currency": "IDR"
},
"virtualAccountTrxType": "c",
"expiredDate": "2023-11-02T17:18:48+07:00",
"additionalInfo": {
"channel": "BSI"
}
}
`;
const body = JSON.parse(payload);
const hashedBody = crypto.createHash('sha256').update(JSON.stringify(body, null, 0)).digest('hex').toLowerCase();
const stringToSign = [
httpMethod,
endpointUrl,
hashedBody,
timestamp
];
let signature = '';
const stringToSign = stringToSign.join(':');
const privKey = fs.readFileSync('./your-private-key-file.pem');
const sign = crypto.createSign('RSA-SHA256');
sign.update(stringToSign);
signature = sign.sign(privKey, 'base64');
console.log('Your Signature:', signature);