Otorisasi Tanda Tangan
Ikhtisar Tanda Tangan Digital
Autentikasi tanda tangan digital merupakan mekanisme keamanan penting untuk semua transaksi API, termasuk operasi Pay-In, Pay-Out, dan Inquiry (inquiry status dan inquiry balance). Proses ini memastikan integritas, keaslian, dan nirsangkal (non-repudiation) permintaan melalui verifikasi kriptografis.
Berlaku untuk semua API yang ditandatangani
Setiap permintaan Pay-In, Pay-Out, dan Inquiry harus menyertakan header X-TIMESTAMP, X-SIGNATURE, dan X-PARTNER-ID. Aturan penandatanganan pada halaman ini sesuai dengan referensi permintaan Pay-In.
Proses Pembuatan Tanda Tangan
Siapkan Data Tanda Tangan
Kumpulkan komponen berikut untuk pembuatan tanda tangan:
- X-TIMESTAMP: Stempel waktu berformat ISO 8601 (sebagai contoh
2024-12-30T18:30:36+07:00) - Merchant Secret: Kunci rahasia merchant yang ditetapkan untuk Anda
- Request Body: Payload JSON (akan diminifikasi)
- Merchant Private Key: Kunci privat RSA Anda untuk penandatanganan (PKCS#8, Base64)

Minifikasi Request Body
Hapus semua spasi dan pemformatan yang tidak perlu dari body permintaan JSON untuk memastikan pembuatan tanda tangan yang konsisten.
{
"area": 10,
"callbackUrl": "https://docs.smilepayz.com/en/",
"merchant": {
"merchantId": "20019"
},
"money": {
"amount": 10000,
"currency": "IDR"
},
"orderNo": "20019c5b63c4b-e34a-4855-9b20-d4b",
"paymentMethod": "QRIS",
"purpose": "Purpose For Transaction from Java SDK"
}
{"area":10,"callbackUrl":"https://docs.smilepayz.com/en/","merchant":{"merchantId":"20019"},"money":{"amount":10000,"currency":"IDR"},"orderNo":"20019c5b63c4b-e34a-4855-9b20-d4b","paymentMethod":"QRIS","purpose":"Purpose For Transaction from Java SDK"}
Jangan ubah body setelah penandatanganan
Buat tanda tangan dari string JSON terminifikasi yang persis sama dengan yang akan dikirim dalam body HTTP. Perubahan apa pun setelah penandatanganan akan menyebabkan verifikasi gagal.
Susun String to Sign
Buat string tanda tangan dengan menggabungkan komponen-komponen menggunakan pemisah pipe (|):
X-TIMESTAMP + "|" + merchant_secret + "|" + minify(requestBody)
Contoh String to Sign
2024-12-30T18:30:36+07:00|95b57c46b8c2e068982be23fb669a80612cad68e6ce6ba4f5af9ec20d23bb274|{"area":10,"callbackUrl":"https://docs.smilepayz.com/en/","merchant":{"merchantId":"20019"},"money":{"amount":10000,"currency":"IDR"},"orderNo":"20019c5b63c4b-e34a-4855-9b20-d4b","paymentMethod":"QRIS","purpose":"Purpose For Transaction from Java SDK"}
Buat Tanda Tangan Digital
Terapkan SHA256withRSA pada string to sign menggunakan kunci privat RSA Anda.
Formula Kriptografis
SHA256withRSA(stringToSign, merchantPrivateKey)
Contoh RSA Private Key (PKCS#8, Base64)
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1d8Zaurk/A0WB1X7CrW+SqsVM4v2ZMGTdqWEQOAKQyY/DZEmgSm7whUMLYfqeKxzzAd3lZHvviyO/V/Ht2i8axtiA5tyDa485IgBy+jO9UHxzI+9CRxnIY5jjDAqAgEvRa0HZEfYHLGlH3IQ4A2a7g8Wm3yqn2prIrgY21nO4+33GuBR0qCpGxth0kV7/RSdhWv3Ff1zkb7yUYn9zfuag9stLf1nw+Ui7hjXL+xGiNRYsDCHOLzKynZxk1cBfqyPY5YMTIjQik4cI+YfM4BMN8bmTB1y3z9sdfbHVTI/PD/fbDzvfK7S/XJvY2bj1TwNeYCHcd5Dx6MYiO12rfA5XAgMBAAECggEASRpMJMat042c1ZXM793wYXQ78SJMKQcOyyDtwhveJLeeG4duBY2WrXvxq0c4L7mMevSYkE1vg+tYZj+mbTwE19Oc6iiWUrbkxo5FAgx7BVvEFpo2S0vbPhepQmXaYTPN1zpPF4QdyXwBmXpjJTFxlD6cRAII6/+rdQZt6G9xxOWUq6bbENNUc6y+N+97oPPwrzLZGYADfusM735Mlx3j8xTGLSHTh2d8e5epwKcSW+ZvBbiM30ZeO+M5bh6zgZrHpPQNJAOsEDuVk5vjuC5O+i81YwP702GAE50zI9Ysr4QMS3QQGe2jwpJAmMjvO6/MRv/6De83NJ6Y70+00OONcQKBgQDtP+sQOyKRErCN9ePmu6FBfzMZcTC5vXQpA0kW7vUMKyQR537appYcOb5DPJDgUqqyNcr8vIqadR6OW0thkfRyb+L/elOyS/XnVbFc6RA9DW/KV4xm68LUX9oRqfgy5YOESEEA2igEh8IdqfkIRCZ3g7xrQUmb3gq0BM2/hnxyHwKBgQDDz0Slbw2Pv820CSheZi8HQf3sEFe0JDHKo2KAHX9rTvFEF3xsT+UZh6pcHUcesYlVlJM2Ciejq4dmiOnvGIKtkqtcLfioVbPrDP2HlDtIT3mSC7sL6VdWLxYz/l0EVjsR2KMVwXD42jcjz3e5bDIi98Z8G4Bx6cu6pwlBtfMMyQKBgHJhWoacu/GNPSlz3sgitX/KP2yIsaEawaH0P4ya7/FJ6z3mibYIkl5RSHVKAd7ke/8nCBIJJBmLVYv0X8dOgreZUasx4qAXFxrMqZHNm1KT4819n+cOywNwosXZWBvRozJnU0+B3cExYljC1ZkyogkErhZXh5Fp32ci2b74q46pAoGANWyixSRkpdjmLf3lpsPohulpd3QKCdtqPmiPonbp4gRa7YIe74po7qtGPvW6BtTXrKLp+2+P2yccvUwTz6l3VhZTbMYaNwuTGEmmDszR0+vjNoa+1QQoURpty2fWRy2j1j2uWWw6/5rCOqILf2rWzxWdcRUr2Wi5rkaOzv/uvYkCgYEAlFV6huNhi1NIIg5gkTS0kmxnIUWu8XIT9IgZWMrhR7TvImVISoormE9LdISgMBz+J5YJxhAZbxVxlUJrsl+HVcS81gmUkeoIpTA+iKUzbD9gAPjEdqVylzN8A9uNIkkb5Cf4BWYfEyMaLfBuiHDlKDcBjz4cJbcom3RVGacghKc=
Contoh Tanda Tangan yang Dihasilkan
BD+qFc0/JOl1grDKSs0zMumPeswzLN/tVj/bms9AYZdVwtzZ3RQJ2gdCxtMeF/fYVTLOZZVLiKnFm0KDjoChl9Kz+6VpgFCMCQt99iUxoJxLtCvPhlCdD1rHVNfrNWKt4mLSNdQWmh//hlwP4AJqidMUKCTDMDm2ivgvQqMIFqdadITAreevKQ+VZs7ukZMzo5BgYfdhCiorAqAb/EUN1/O0pDt8Uv+xcL4QNxxu1dj10UDRbHaox4Qv1tfXbRoiisNa9xAlVkd3l19FW/Mv09jkDEKL+x5uIx3WjGY81NyVW6yxx5jEbsZbdl5KfqA6SwKpc3IOVLwR4J4Q0jaFGA==
Contoh Kode
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
public class SignatureExample {
/** Build X-SIGNATURE for Smilepayz APIs. */
public static String sign(
String timestamp,
String merchantSecret,
String minifiedRequestBody,
String privateKeyBase64) throws Exception {
String stringToSign = timestamp + "|" + merchantSecret + "|" + minifiedRequestBody;
byte[] keyBytes = Base64.getDecoder().decode(privateKeyBase64);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(stringToSign.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(signature.sign());
}
}
import base64
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
def sign(timestamp: str, merchant_secret: str, minified_body: str, private_key_base64: str) -> str:
"""Build X-SIGNATURE for Smilepayz APIs."""
string_to_sign = f"{timestamp}|{merchant_secret}|{minified_body}"
key_bytes = base64.b64decode(private_key_base64)
private_key = serialization.load_der_private_key(key_bytes, password=None)
signature = private_key.sign(
string_to_sign.encode("utf-8"),
padding.PKCS1v15(),
hashes.SHA256(),
)
return base64.b64encode(signature).decode("utf-8")
const crypto = require("crypto");
/**
* Build X-SIGNATURE for Smilepayz APIs.
* @param {string} timestamp - X-TIMESTAMP value
* @param {string} merchantSecret - merchant secret
* @param {string} minifiedBody - minified JSON request body
* @param {string} privateKeyBase64 - PKCS#8 private key (Base64)
*/
function sign(timestamp, merchantSecret, minifiedBody, privateKeyBase64) {
const stringToSign = `${timestamp}|${merchantSecret}|${minifiedBody}`;
const privateKey = crypto.createPrivateKey({
key: Buffer.from(privateKeyBase64, "base64"),
format: "der",
type: "pkcs8",
});
const signer = crypto.createSign("RSA-SHA256");
signer.update(stringToSign, "utf8");
return signer.sign(privateKey, "base64");
}
<?php
/**
* Build X-SIGNATURE for Smilepayz APIs.
*/
function sign(
string $timestamp,
string $merchantSecret,
string $minifiedBody,
string $privateKeyBase64
): string {
$stringToSign = $timestamp . '|' . $merchantSecret . '|' . $minifiedBody;
$keyPem = "-----BEGIN PRIVATE KEY-----\n"
. chunk_split($privateKeyBase64, 64, "\n")
. "-----END PRIVATE KEY-----\n";
$privateKey = openssl_pkey_get_private($keyPem);
openssl_sign($stringToSign, $signature, $privateKey, OPENSSL_ALGO_SHA256);
return base64_encode($signature);
}
using System;
using System.Security.Cryptography;
using System.Text;
public static class SignatureExample
{
/// <summary>Build X-SIGNATURE for Smilepayz APIs.</summary>
public static string Sign(
string timestamp,
string merchantSecret,
string minifiedBody,
string privateKeyBase64)
{
var stringToSign = $"{timestamp}|{merchantSecret}|{minifiedBody}";
var keyBytes = Convert.FromBase64String(privateKeyBase64);
using var rsa = RSA.Create();
rsa.ImportPkcs8PrivateKey(keyBytes, out _);
var data = Encoding.UTF8.GetBytes(stringToSign);
var signature = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
return Convert.ToBase64String(signature);
}
}
package signature
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
)
// Sign builds X-SIGNATURE for Smilepayz APIs.
func Sign(timestamp, merchantSecret, minifiedBody, privateKeyBase64 string) (string, error) {
stringToSign := timestamp + "|" + merchantSecret + "|" + minifiedBody
keyBytes, err := base64.StdEncoding.DecodeString(privateKeyBase64)
if err != nil {
return "", err
}
privateKey, err := x509.ParsePKCS8PrivateKey(keyBytes)
if err != nil {
return "", err
}
rsaKey := privateKey.(*rsa.PrivateKey)
hash := sha256.Sum256([]byte(stringToSign))
signature, err := rsa.SignPKCS1v15(rand.Reader, rsaKey, crypto.SHA256, hash[:])
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(signature), nil
}
Sertakan Tanda Tangan dalam Header HTTP
Tambahkan tanda tangan yang dihasilkan ke header permintaan HTTP bersama dengan stempel waktu dan merchant ID:
Content-Type: application/json
X-TIMESTAMP: 2024-12-30T18:30:36+07:00
X-SIGNATURE: BD+qFc0/JOl1grDKSs0zMumPeswzLN/tVj/bms9AYZdVwtzZ3RQJ2gdCxtMeF/fYVTLOZZVLiKnFm0KDjoChl9Kz+6VpgFCMCQt99iUxoJxLtCvPhlCdD1rHVNfrNWKt4mLSNdQWmh//hlwP4AJqidMUKCTDMDm2ivgvQqMIFqdadITAreevKQ+VZs7ukZMzo5BgYfdhCiorAqAb/EUN1/O0pDt8Uv+xcL4QNxxu1dj10UDRbHaox4Qv1tfXbRoiisNa9xAlVkd3l19FW/Mv09jkDEKL+x5uIx3WjGY81NyVW6yxx5jEbsZbdl5KfqA6SwKpc3IOVLwR4J4Q0jaFGA==
X-PARTNER-ID: 20019
Contoh cURL (Sandbox Pay-In)
curl -X POST 'https://sandbox-gateway.smilepayz.com/v2.0/transaction/pay-in' \
-H 'Content-Type: application/json' \
-H 'X-TIMESTAMP: 2024-12-30T18:30:36+07:00' \
-H 'X-SIGNATURE: BD+qFc0/JOl1grDKSs0zMumPeswzLN/tVj/bms9AYZdVwtzZ3RQJ2gdCxtMeF/fYVTLOZZVLiKnFm0KDjoChl9Kz+6VpgFCMCQt99iUxoJxLtCvPhlCdD1rHVNfrNWKt4mLSNdQWmh//hlwP4AJqidMUKCTDMDm2ivgvQqMIFqdadITAreevKQ+VZs7ukZMzo5BgYfdhCiorAqAb/EUN1/O0pDt8Uv+xcL4QNxxu1dj10UDRbHaox4Qv1tfXbRoiisNa9xAlVkd3l19FW/Mv09jkDEKL+x5uIx3WjGY81NyVW6yxx5jEbsZbdl5KfqA6SwKpc3IOVLwR4J4Q0jaFGA==' \
-H 'X-PARTNER-ID: 20019' \
-d '{"area":10,"callbackUrl":"https://docs.smilepayz.com/en/","merchant":{"merchantId":"20019"},"money":{"amount":10000,"currency":"IDR"},"orderNo":"20019c5b63c4b-e34a-4855-9b20-d4b","paymentMethod":"QRIS","purpose":"Purpose For Transaction from Java SDK"}'
Base URL Sandbox
Sandbox: https://sandbox-gateway.smilepayz.com · Produksi: https://gateway.smilepayz.com. Lihat URL lingkungan.
Verifikasi Tanda Tangan
Gunakan kunci publik RSA di bawah ini untuk memverifikasi callback atau untuk memvalidasi tanda tangan selama pengujian integrasi.
RSA Public Key untuk Verifikasi
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtXfGWrq5PwNFgdV+wq1vkqrFTOL9mTBk3alhEDgCkMmPw2RJoEpu8IVDC2H6nisc8wHd5WR774sjv1fx7dovGsbYgObcg2uPOSIAcvozvVB8cyPvQkcZyGOY4wwKgIBL0WtB2RH2ByxpR9yEOANmu4PFpt8qp9qayK4GNtZzuPt9xrgUdKgqRsbYdJFe/0UnYVr9xX9c5G+8lGJ/c37moPbLS39Z8PlIu4Y1y/sRojUWLAwhzi8ysp2cZNXAX6sj2OWDEyI0IpOHCPmHzOATDfG5kwdct8/bHX2x1UyPzw/32w873yu0v1yb2Nm49U8DXmAh3HeQ8ejGIjtdq3wOVwIDAQAB
Alat Pengembangan
Alat Minifikasi JSON
| Alat | URL | Penggunaan |
|---|---|---|
| JSON Formatter | https://jsonformatter.org/json-minify | Pilih opsi "Minify JSON" |
Praktik Terbaik Keamanan
Manajemen Kunci
- Penyimpanan Aman: Simpan kunci privat dalam penyimpanan terenkripsi yang aman
- Rotasi Kunci: Rotasi pasangan kunci RSA secara berkala
- Kontrol Akses: Batasi akses ke kunci privat hanya untuk personel yang berwenang
- Pencadangan: Pelihara cadangan kunci privat yang aman
Pedoman Implementasi
Jendela stempel waktu
Pastikan X-TIMESTAMP berada dalam rentang ±5 menit dari waktu server Smilepayz. Sinkronkan jam sistem Anda dengan server NTP.
Integritas permintaan
Jangan pernah mengubah body permintaan setelah tanda tangan dibuat. Buat ulang tanda tangan jika ada field yang berubah.
- Penanganan Kesalahan: Terapkan penanganan kesalahan yang tepat untuk kegagalan validasi tanda tangan
- Pencatatan Log: Catat log upaya pembuatan tanda tangan untuk keperluan audit (jangan pernah mencatat merchant secret atau kunci privat)
Masalah Umum dan Solusinya
- Ketidakcocokan Tanda Tangan: Verifikasi semua komponen berada dalam urutan dan format yang benar
- Masalah Stempel Waktu: Pastikan jam sistem disinkronkan dengan server NTP
- Format Kunci: Verifikasi format dan pengodean kunci RSA (kunci privat PKCS#8, Base64)
- Masalah Pengodean: Pastikan pengodean UTF-8 yang benar untuk semua komponen string
Kepatuhan dan Standar
Standar Kriptografis
- Algoritma: SHA256withRSA (hash SHA-256 dengan tanda tangan RSA)
- Ukuran Kunci: Disarankan kunci RSA minimal 2048-bit
- Pengodean: Pengodean Base64 untuk keluaran tanda tangan
- Format: Format PKCS#8 untuk kunci privat
Persyaratan Audit
- Log Tanda Tangan: Pelihara log semua pembuatan dan verifikasi tanda tangan
- Penggunaan Kunci: Lacak penggunaan kunci dan jadwal rotasi
- Log Akses: Pantau akses ke materi kriptografis
- Laporan Kepatuhan: Buat laporan kepatuhan untuk persyaratan regulasi
