Signature Generation
The signature is used to authenticate requests sent to the SNAP API. Signatures are generated from data sent to the Snap API.
Winpay uses the type Asymmetric Without Get Token
to generate signatures, so merchants do not need to request get tokens first to create signatures.
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');
const encodedSignature = Buffer.from(signature).toString('base64');
console.log('Your Signature:', encodedSignature);