Signature Generation
Signature digunakan untuk mengautentikasi request yang dikirimkan ke SNAP API. Signature dihasilkan dari data-data yang dikirimkan ke Snap API.
Winpay menggunakan type Asymmetric Without Get Token
untuk menghasilkan signature, sehingga merchant tidak perlu melakukan request get token terlebih dahulu untuk membuat signature.
Private Key & Public Key
Sebelum dapat menghasilkan signature, merchant harus memiliki RSA private key dan public key. Untuk mendapatkan private key dan public key tersebut merchant bisa generate sendiri atau menggunakan private key dan public key dari situs pihak ketiga seperti: https://cryptotools.net/rsagen.
Rumus Signature
Signature dihasilkan dari data-data yang dikirimkan ke Snap API. Berikut adalah rumus untuk menghasilkan signature:
stringToSign = HTTPMethod +":"+ EndpointUrl +":"+ Lowercase(HexEncode(SHA-256(minify(RequestBody)))) + ":" + TimeStamp
signature = base64_encode(SHA256withRSA(private_key, stringToSign))
Source Code Pembuatan Signature
- 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);