Payment Gateway

Asymmetric Signature

Asymmetric Signature a cryptographic method that uses public and private keys to create and verify signatures. The private key creates the signature, while the public key verifies it. In its implementation, creating a signature using a private key is applied to services with the Merchant -> Espay flow. In contrast, signature validation using a public key is implemented for services with the Espay -> Merchant flow.
  • SHA256withRSA is used to generate a signature with a private key.

X-SIGNATURE = base64Encode(SHA256withRSA(PrivateKey, StringToSign))
  • SHA256withRSA is used to validate signature with public key.
Base64Decode(X-SIGNATURE) = SHA256withRSA(PublicKey, StringToSign)
  • StringToSign
StringToSign = HTTPMethod+”:”+RelativeUrl+”:”+ Lowercase((SHA-256(MinifyJson(RequestBody))))+”:”+Timestamp 
Asymmetric Signature Component
Component Description
HTTP Method
HTTP Method.

Example:
POST, DELETE
Relative URL
API URL path of the service you are using.

Example:
  • API URL:
    https://sandbox-api.espay.id/api/v1.0/qr/qr-mpm-generate
  • Relative URL:
    /api/v1.0/qr/qr-mpm-generate
Request Body
Request Body of the service in JSON format.
Timestamp
Customer's current local date and time, same as X-TIMESTAMP Header.

Example:
yyyy-MM-ddThh:mi:ssTZD

Format:
2023-08-31T07:49:28+07:00
Generate Private Key dan Public Key (PKCS#1)

Generate Private Key and Public Key (PKCS#1)
You can generate a private and public key in PKCS#1 format for production use with OpenSSL. For development purposes, you may use the provided sample private and public keys.

Example of generate private key and public key with PKCS#1 format

With openSSL 1.1.1(PKCS#1):

openssl genrsa -out private.pem 2048

openssl rsa -in private.pem -pubout > public.pub
Sample Private Key and Public Key (PKCS#1)
Sample public key format PKCS#1

-----BEGIN PUBLIC KEY----- 
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2O9xDMTBiZ5oOy3LBVn6 
TerxWMHEwxl6gr0SX1dRt4be5vq2voFMoCHokeowqpeU5ZQi0EM36W7Q1K8hH6KR 
jdNqhdIHyMh7X0yhVJTQ3Fz9QcjBfeMwoovmIYHP+U08GKz7j99VojSSriYvzT1m 
PdwvTuAdFT3QEXfgdMLKQCjtXF/eyg2Q+xCYJALv+zeaPlsu00RO3TM5NGaCSbFC 
oF/xa4IOfV+215beBvl1fUhW6mkEo7gdhK8T0ddk5bInEJs3YzDwQNtAutLEFVot 
EKX2ETqIk8S1H7Pou7tSo73O0fFGaSBhG610bKIb9lLTXCQYJKk8bygPaL3aoT+5 
QwIDAQAB
-----END PUBLIC KEY-----
Sample private key format PKCS#1

-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA2O9xDMTBiZ5oOy3LBVn6TerxWMHEwxl6gr0SX1dRt4be5vq2
voFMoCHokeowqpeU5ZQi0EM36W7Q1K8hH6KRjdNqhdIHyMh7X0yhVJTQ3Fz9QcjB
feMwoovmIYHP+U08GKz7j99VojSSriYvzT1mPdwvTuAdFT3QEXfgdMLKQCjtXF/e
yg2Q+xCYJALv+zeaPlsu00RO3TM5NGaCSbFCoF/xa4IOfV+215beBvl1fUhW6mkE
o7gdhK8T0ddk5bInEJs3YzDwQNtAutLEFVotEKX2ETqIk8S1H7Pou7tSo73O0fFG
aSBhG610bKIb9lLTXCQYJKk8bygPaL3aoT+5QwIDAQABAoIBAQDTSPIcc43kUWpH
KSSxQ59sQEVsIt1W//u4VhoMzekDDNMQuGNATIKq/Bud8jAQFq6oo4z8tltAefPf
Eer6+sU1ExKO369BOTIf8Wy4CnEaD1+CsNrzl1EJH6S2Qc6jizva9K/WwriO0RGD
mCG6jfCEk21oLxNkWt3KBa2RSx7dOLO+ct07jtRbfYCVCAezyx6fWxLJ6eVmGZXM
kOhAr9tQ6IC3v/iQgA00LNPXR+X12obcmNXtcng5uHffeZNr6tmpLpXTYLdwZlwl
FINuTGpPjp1yy6q6GQYphF51ywRFN17g8NoVHLXDAfnrmB1lgtbC3nSiAvqEq2c6
XQkAIZbBAoGBAPgQDtG7RJ/Wdo5ra9HMgceVqDQgrdY4vw4cnV4NVGSBGn8jNhk7
YrJ8siJbLxqi5cPwJzu7xS8krKyt3vBY8AFKvVJ9yZ06VVL4d2LvWr50ym/zshnC
w1WlKhcuyaqP6MCiC6pZNA5LR1AN6hK2B1ZnmSrvDkg+MZtGTAxJrF6TAoGBAN/g
aVtaHuw1Zh2ixRfUjjQ4YMSxt/68DnmAJemmWQysvFsTZLfy87KLenmLABnG9qke
sOLD/vC7h5s5G9+vN4JMbmTYGBYp0VW5wWaC7Nw8cskgsmb7BZ+K7HsQbmtxh9Nu
BeQqdmQHZvLQ6wgY+0QTy/1KTUPwxLztyJttGjiRAoGAQEkpDgFSD3osz0vXbU9q
cqa+KIQviMy79pRD1BPwQvuSOlCNvIw/T7IxF+Y5ltWQZe7evAQ1XbpLZZTJqc/i
ovMTjUU78psjcZUim2kcQy9RJyIojbSDmrZq6gceDC2vS/yyuTrU2r93g6+XcbHq
xOGkOBQrx10Wzf6xxp1xJjECgYBfSk6t4nsdAVGYtap8jS2GDqUps5dkZrkmgCQj
AnoOygtWHLgXD+MokPOtfjupvSVKMNULgG8oGjoLGNDDcfoHjO7EH7KI5H3Epk8q
ifm1eElHUJJ/AMOQ9/nWG9VUCDvPA5qgVm6T/w6TtdcEWFXC0UZXZmPi0j17SR7F
AThS8QKBgQCCyPFJzwGIP99PcakQ38oFcoU8u/ahb0ghgJfSgK+K/ChXSyfbq5zt
jRkj6UWLa3plYX3po9h0Yp6f2IxnbOa3VK6fPkcSvBxhgK3RrugPerUJzFEPd3k4
GTqOBXtXO6N7zEMYxZxv0SgrV24LPfPz0aPObDeH6F0kuzXjanopIw== 
-----END RSA PRIVATE KEY-----

Generate Signature

Signature Generation is the process of creating a digital signature by hashing data with the SHA-256 algorithm, then signing the hash with a private key. This signature generation is used for services that follow the Merchant → Espay flow.

Steps to Generate a Signature

The following are the steps to generate a signature with a private key:

  1. Create StringToSign Component
  • Minify Request Body.
MinifyJson(RequestBody))
Example:
Format before minify
{
  "partnerReferenceNo": "DIGORDER000001",
  "merchantId": "SGWDIGALLERY",
  "subMerchantId": "fd322d0f036c8443d6904973c1a329bd", 
  "amount": {
    "value": "10000.00",
    "currency": "IDR"
  },
  "urlParam": {
    "url": "https://yourthankyoupage.com",
    "type": "PAY_RETURN",
    "isDeeplink": "N"
  },
  "validUpTo": "2023-12-23T07:44:11+07:00",
  "pointOfInitiation": "Website",
  "payOptionDetails": {
    "payMethod": "014",
    "payOption": "BCAATM",
    "transAmount": {
      "value": "10000.00",
      "currency": "IDR"
    },
    "feeAmount": {
      "value": "10000.00",
      "currency": "IDR"
    }
  },
  "additionalInfo": {
    "payType": "REDIRECT",
    "userId": "425666",
    "userName": "Agung Setiadi Putra",
    "userEmail": "[email protected]",
    "userPhone": "082231838297",
    "buyerId": "12345678"
  }
}

Format after minify
{"partnerReferenceNo":"DIGORDER000001","merchantId":"SGWDIGALLERY","subMerchantId":"fd322d0f036c8443d6904973c1a329bd","amount":{"value":"10000.00","currency":"IDR"},"urlParam":{"url":"https://yourthankyoupage.com","type":"PAY_RETURN","isDeeplink":"N"},"validUpTo":"2023-12-23T07:44:11+07:00","pointOfInitiation":"Website","payOptionDetails":{"payMethod":"014","payOption":"BCAATM","transAmount":{"value":"10000.00","currency":"IDR"},"feeAmount":{"value":"10000.00","currency":"IDR"}},"additionalInfo":{"payType":"REDIRECT","userId":"425666","userName":"Agung
Setiadi Putra","userEmail":"[email protected]","userPhone":"082231838297","buyerId":"12345678"}}
  • Encrypt the minified request body using SHA-256
(SHA-256(MinifyJson(RequestBody)))
Example:
Format before encryption
{"partnerReferenceNo":"DIGORDER000001","merchantId":"SGWDIGALLERY","subMerchantId":"fd322d0f036c8443d6904973c1a329bd","amount":{"value":"10000.00","currency":"IDR"},"urlParam":{"url":"https://yourthankyoupage.com","type":"PAY_RETURN","isDeeplink":"N"},"validUpTo":"2023-12-23T07:44:11+07:00","pointOfInitiation":"Website","payOptionDetails":{"payMethod":"014","payOption":"BCAATM","transAmount":{"value":"10000.00","currency":"IDR"},"feeAmount":{"value":"10000.00","currency":"IDR"}},"additionalInfo":{"payType":"REDIRECT","userId":"425666","userName":"Agung
Setiadi Putra","userEmail":"[email protected]","userPhone":"082231838297","buyerId":"12345678"}}
Format after encryption
f6bbc08be6997d4bd02af5254e3f934f9ed908fb7724d2e8cf98b178158a2b7a
  • Change the request body encrypted with SHA-256 to Hex Encode and Lowercase.
HexEncode is optional, but use it if the SHA-256 returns a binary stream.
Using HexEncode
Lowercase(HexEncode(SHA-256(MinifyJson(RequestBody))))
Not using HexEncode
Lowercase((SHA-256(MinifyJson(RequestBody))))
Example:
Format before HexEncode dan Lowercase
f6bbc08be6997d4bd02af5254e3f934f9ed908fb7724d2e8cf98b178158a2b7a
Format after lowercase (Not using HexEncode)
f6bbc08be6997d4bd02af5254e3f934f9ed908fb7724d2e8cf98b178158a2b7a
  1. Generate StringToSign
To create a StringToSign, combine all components using a colon “:”.

StringToSign

HTTPMethod+”:”+RelativeUrl+”:”+ Lowercase(HexEncode(SHA-256(MinifyJson(RequestBody))))+”:”+Timestamp

Example:
Format StringToSign
POST:/apimerchant/v1.0/debit/payment-host-to-host:f6bbc08be6997d4bd02af5254e3f934f9ed908fb7724d2e8cf98b178158a2b7a:2024-03-14T07:49:28+07:00
  1. Generate X-Signature with private key
X-SIGNATURE = base64Encode(SHA256withRSA(PrivateKey, StringToSign))
Example:
Format X-Signature
D/QV3mN8i19xZRTkOW5sdn5XtrXoT8EmepDRzaGHheT+qnnzrZlEKCBic6M5sQyj6Hp8jFSY4PCsMm7lJQFRLiGPdYf/rDPFsa/ai1MnoUoMKUFSmQHUmjAAhbQjkdNWKjoSG+xTTmyEzsBz6/P6ijWMBDTZWPIb3/qaN6oxcnhw2RLOCyCZlXwBeP6RMc3Gz1wilRGQ5jqeebQVGgUJjqAGLM/cVIjG0fXmQAmsG0g3XA7e63qW0M6am8zXHPtumRF5X4JN0CSRcV9QjvLvH21vcnYhuixebzr5dnnoroXL/aE/ptfrb79Ou0dwqRsQBCqZhwFssSFRPDhzqsZWIw==
Sample Code


PHP - Generate Signature

$stringToSign = $http_method.':'.$endpoint_url.':'.strtolower((hash('sha256', json_encode(json_decode($_POST['bodyRequest']),JSON_UNESCAPED_SLASHES)))).':'.$timestamp;

openssl_sign($stringToSign, $signature, $privKey, 'sha256WithRSAEncryption'); 
            
Try It!

You can enter data using the input provided. After that, you submit and see the results!

Request
Value
Response

Validasi Signature

Signature Validation is the process of verifying the authenticity of a digital signature by decoding it from Base64 and validating it with a public key. This signature validation is used for services that follow the Espay → Merchant flow.

Steps to Validate a Signature
The following are the steps for validating a signature with a public key:
  1. Decode X-Signature Header
Example:
Format X-Signature
rgfRxIG62kOVexmBsrHnl87aW1lS+JtvMUa9pF8yhHb+m1Rv63LzFFC50FTzZMhZIarrI4Tff4Q3RhvMP5nLEMwOamnVPHtYnIY9Xjvudz3AitjUU1010dGOn7vt8ojY8K4kN+extwGuxmmPePbYksy4UGs8Ll8SfwksOKgygzFy+AttZY2s2duAt8tD/D+q576j62CyOVRvMVysXVWCRnYxPBa8D9hUj+M47yxdYN21RteSkQjB90fBXAVeBeikOzosDflaO2PH80grbmKSV5hzF9Z48ABnDxkwFG7PG8cqK1XRde34aXFYsI+sXCQDLZ6Y3TWBA/iWfn1lx08T3g==
X-Signature format after Base64Decode
āC{[YRo1F_2vTorPTdY!#7F?ji<{X=^;w=S]5ю$7籷ix̸ؒPk<._ ,821r meۀC?羣`9To1\]UFv1<T8,]`ݵFגG\^;:,
Z;cH+nbWsx�g0n*+UuiqX\$-5~}eO
  1. Create StringToSign Component
  • Minify Request Body.
MinifyJson(RequestBody))
Example:
Format before minify
{
  "partnerServiceId": " Espay",
  "customerNo": "SGWDIGALLERY",
  "virtualAccountNo": "DIGORDER000002",
  "trxDateInit": "2024-06-17T21:45:46+0700",
  "inquiryRequestId": "710c1836-eddf-49d1-8234-f11e93750fdd"
}
Format after minify
{"partnerServiceId":" Espay","customerNo":"SGWDIGALLERY","virtualAccountNo":"DIGORDER000002","trxDateInit":"2024-06-17T21:45:46+0700","inquiryRequestId":"710c1836-eddf-49d1-8234-f11e93750fdd"}
  • Encrypt the minified request body using SHA-256
(SHA-256(MinifyJson(RequestBody)))
Example:
Format before encryption
{"partnerServiceId":" Espay","customerNo":"SGWDIGALLERY","virtualAccountNo":"DIGORDER000002","trxDateInit":"2024-06-17T21:45:46+0700","inquiryRequestId":"710c1836-eddf-49d1-8234-f11e93750fdd"}
Format after encryption
33578ff224ac535c2be314623a3ba420f6b965f4570ec9bbb8af17ac8dbd6468
  • Change the request body encrypted with SHA-256 to Hex Encode and Lowercase.

HexEncode is optional, but use it if the SHA-256 returns a binary stream.
Using HexEncode
Lowercase(HexEncode(SHA-256(MinifyJson(RequestBody))))
Not using HexEncode
Lowercase((SHA-256(MinifyJson(RequestBody))))
Example:
Format before HexEncode dan Lowercase
33578ff224ac535c2be314623a3ba420f6b965f4570ec9bbb8af17ac8dbd6468
Format after lowercase (Not using HexEncode)
33578ff224ac535c2be314623a3ba420f6b965f4570ec9bbb8af17ac8dbd6468
  1. Generate StringToSign
To create a StringToSign, combine all components using a colon “:”.

StringToSign

HTTPMethod+”:”+RelativeUrl+”:”+ Lowercase(HexEncode(SHA-256(MinifyJson(RequestBody))))+”:”+Timestamp

Example:
Format StringToSign
POST:/api/webhooks/epsay/v1.0/transfer-va/inquiry.php:33578ff224ac535c2be314623a3ba420f6b965f4570ec9bbb8af17ac8dbd6468:2024-06-17T21:45:46+0700
  1. Generate X-Signature using a private key
X-SIGNATURE =
Base64Decode(X-SIGNATURE) = SHA256withRSA(PublicKey, StringToSign)
Example:
Signature validation format
1
Sample Code


PHP - Signature Validation

// Decode X-Signature
$requestSignature = base64_decode($xSignature);

// String to Sign
$stringToSign = $http_method.':'.$endpoint_url.':'.strtolower((hash('sha256', json_encode(json_decode($_POST['bodyRequest']),JSON_UNESCAPED_SLASHES)))).':'.$timestamp;

// Signature validation
$signVerify = openssl_verify($stringToSign, $requestSignature, $publicKey, 'sha256WithRSAEncryption');    
            
Try It!

You can enter data using the input provided. After that, you submit and see the results!

Request
Value
Request

FAQ

Asymmetric Signature works through two main processes:
  1. Generate signature:
    The process of generating a digital signature using a private key when a request is sent from the Merchant to Espay.
  2. Signature validation:
    The process of verifying the digital signature using a public key occurs when the Merchant receives a request from Espay.

The use of a signature depends on the direction of the request flow.

  • Signature generation is required for requests in the Merchant → Espay flow.
  • Signature validation is required for requests in the Espay → Merchant flow.

Please ensure that you follow the correct flow for each service.
For Inquiry and Payment services, signature validation is not mandatory.

A private key and a public key are a key pair used in the RSA mechanism for signing and verifying signatures. You can generate this key pair using OpenSSL in PKCS#1 format. Learn more about how to generate a private key and public key using OpenSSL here.

Merchants only need to generate their own public and private keys when moving to the production environment. For sandbox purposes, you may use the sample keys provided in the Espay documentation.

See the sample private key and public key here.

Note:
Sample keys are intended for sandbox use only and must not be used in the production environment.

The “Unauthorized. Invalid Signature” error occurs when the X-SIGNATURE value you send does not comply with the Espay SNAP specification or does not match the request received by the system
Please recheck the following items:
  • Ensure the StringToSign format is correct.
  • Ensure the request body is minified.
  • Ensure the hashed body uses lowercase characters.
  • Ensure the relative URL matches the endpoint.
  • Ensure the timestamp in the header matches the timestamp in the StringToSign.
  • Ensure the private key used is correct.
You can use the Espay Signature Generator to verify that the generated signature is correct.
If the signature is valid but the request is still rejected, please contact the Espay support team.
Scroll to Top