Signature Algorithm
{% raw %}
Signature Algorithm
Signature algorithm is used to sign your payment API request with a private key to obtain additional security.
Data object needs to be sorted, the Nested object also needs to be sorted.
Step 1 — Prepare a Request Parameter
Method: POST
Refer to which API endpoint you are calling. Below request parameter is just an EXAMPLE.
Example of Web/Mobile Payment
Request Body Parameters
Object of order
Example: (Refer to explanation below)
Object of customer
Example: (Refer to explanation below)
RM currently supported method
Example: []
Object of type. See Type Object below.
Example: (Refer to explanation below)
ID of the store to create QR code
Example: "10946114768247530"
URL to redirect after payment is made
Example: "https://google.com"
Webhook URL that RM will call with the payment result
Example: "https://google.com"
Select layout for Web payment. v1 / v2 (Credit Card) / v3 (Credit Card and FPX)
Example: "v3"
Order object (order):
Order title. Max 32 characters.
Example: "Sales"
Order detail. Max 600 characters.
Example: "1 x iPhone X; 2 x SAMSUNG S8"
Additional order description.
Example: "Sales"
Amount of order in cents. Minimum is RM 0.10 (amount: 10). Required only when isPrefillAmount = true.
Example: 100
Currency notation. Currently only MYR is supported.
Example: "MYR"
Your internal order reference ID.
Example: "6170506694335521334"
Customer object (customer):
Required if tokenization is enabled.
Example: "13245876"
Customer email address.
Example: ""
Customer country code.
Example: ""
Customer phone number.
Example: ""
Type object (type):
Use WEB_PAYMENT for browser-based payments.
Example: "WEB_PAYMENT"
Use MOBILE_PAYMENT for mobile app payments.
Example: "MOBILE_PAYMENT"
Example Request
1{2 "order": {3 "title": "hello",4 "detail": "",5 "additionalData": "world",6 "amount": 10,7 "currencyType": "MYR",8 "id": "7211"9 },10 "customer": {11 "userId": "13245876",12 "email": ""13 },14 "method": [],15 "type": "WEB_PAYMENT",16 "storeId": "1608123035564538121",17 "redirectUrl": "https://revenuemonster.my",18 "notifyUrl": "https://dev-rm-api.ap.ngrok.io",19 "layoutVersion": "v3"20}
Before signing, you must:
- Sort all JSON keys alphabetically — including nested objects
- Make the JSON compact — remove extra spaces and newlines
- Replace special characters:
| Character | Replace With |
|---|---|
< | \u003c |
> | \u003e |
& | \u0026 |
Step 2 — Base64-encode the Data
Take the compact, sorted JSON string and Base64-encode it.
ewogICAgIm9yZGVyIjogewogICAgCSJ0aXRsZSI6ICJoZWxsbyIsCiAgICAJImRldGFpbCI6ICIiLAogICAgCSJhZGRpdGlvbmFsRGF0YSI6ICJ3b3JsZCIsCgkgICAgImFtb3VudCI6IDEwLAoJICAgICJjdXJyZW5jeVR5cGUiOiAiTVlSIiwKCSAgICAiaWQiOiAgIjcyMTEiCiAgICB9LAogICAgImN1c3RvbWVyIjogewogICAgInVzZXJJZCI6ICIiLAogICAgImVtYWlsIjogIiIKfSwKICAgICJtZXRob2QiOltdLAogICAgInR5cGUiOiAiV0VCX1BBWU1FTlQiLAogICAgInN0b3JlSWQiOiAiMTYwODEyMzAzNTU2NDUzODEyMSIsCiAgICAicmVkaXJlY3RVcmwiOiAiaHR0cHM6Ly9yZXZlbnVlbW9uc3Rlci5teSIsCiAgICAibm90aWZ5VXJsIjogImh0dHBzOi8vZGV2LXJtLWFwaS5hcC5uZ3Jvay5pbyIsCiAgICAibGF5b3V0VmVyc2lvbiI6InYzIgp9
Step 3 — Construct the Signing String
- If the body is empty, the
dataparameter can be skipped. - If you are verifying a callback signature, the
requestUrlcan be skipped.
Base64-encoded data body from Step 2.
Example: (See Step 2 output)
HTTP call method used.
Example: "post"
A unique random string. Must not be reused within 120 seconds.
Example: "VYNknZohxwicZMaWbNdBKUrnrxDtaRhN"
The exact API URL being called, including the full path.
Example: "https://sb-open.revenuemonster.my/v3/payment/online"
The signing algorithm to use.
Example: "sha256"
UNIX timestamp of the request. Must be within 120 seconds of server time.
Example: "1527407052"
data=ewogICAgIm9yZGVyIjog...&method=post&nonceStr=VYNknZohxwicZMaWbNdBKUrnrxDtaRhN&requestUrl=https://sb-open.revenuemonster.my/v3/payment/online&signType=sha256×tamp=1527407052
Step 4 — Sign with Your Private Key
Sign the plain text string from Step 3 using sha256 with your RSA private key.
Sign this content using sha256 with your private key and make sure the public key has been uploaded to the RM Merchant Portal.
sha256 IrBg6t73VsH7ieEnQDB4CXHFjMWUkp8Dtddpxqw+4Gvz6Tag7Dx6nrfAt2ofYK8xZN9aBCvAKAfmAOGWIXnsTXfhFBnMA2kadiga7ufUJ81ozyhllbiliRM2ugw1OcqSTLRHWBPhrVwhHBxgDiG9wbuI3FKURrz+CufYYakFoCw=
Step 5 — Add Headers to Your Request
Place the signature in the X-Signature header, prefixed with the sign type:
| Header | Value |
|---|---|
X-Signature | sha256 <your_signature> |
X-Nonce-Str | The nonce string you generated |
X-Timestamp | The Unix timestamp you generated |
Authorization | Bearer <access_token> |
Content-Type | application/json |
Example cURL Request
curl --request POST \--url 'https://sb-open.revenuemonster.my/v3/payment/online' \--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjIwMTgtMDMtMTMiLCJ0eXAiOiJKV1QifQ...' \--header 'Content-Type: application/json' \--header 'X-Nonce-Str: VYNknZohxwicZMaWbNdBKUrnrxDtaRhN' \--header 'X-Signature: sha256 IrBg6t73VsH7ieEnQDB4CXHFjMWUkp8Dtddpxqw+4Gvz6Tag7Dx6nrfAt2ofYK8xZN9aBCvAKAfmAOGWIXnsTXfhFBnMA2kadiga7ufUJ81ozyhllbiliRM2ugw1OcqSTLRHWBPhrVwhHBxgDiG9wbuI3FKURrz+CufYYakFoCw=' \--header 'X-Timestamp: 1527407052' \--data '{"order": {"title": "hello","detail": "","additionalData": "world","amount": 10,"currencyType": "MYR","id": "7211"},"customer": {"userId": "13245876","email": ""},"method": [],"type": "WEB_PAYMENT","storeId": "1608123035564538121","redirectUrl": "https://revenuemonster.my","notifyUrl": "https://dev-rm-api.ap.ngrok.io","layoutVersion": "v3"}'
Response Parameters
Response payload. See Item object below.
Example: (Refer to explanation below)
SUCCESS if the call succeeded. Otherwise returns an error code object. See Appendix: Error Codes.
Example: "SUCCESS"
Item object (item):
Code to identify the web payment session.
Example: "1548316308361173347"
Checkout URL. To change the base URL, replace the domain to your desired URL.
Example: "https://sb-pg.revenuemonster.my/checkout?checkoutId=1548316308361173347"
Example Response
1{2 "item": {3 "checkoutId": "1548316308361173347",4 "url": "https://sb-pg.revenuemonster.my/checkout?checkoutId=1548316308361173347"5 },6 "code": "SUCCESS"7}
Set Up in the Merchant Portal
Step 1 — Create a New Application
Go to Merchant Portal → Developer → Applications and create a new application:

Step 2 — Obtain Credentials
Click on the application you created. You can edit and update relevant information here.
To disable the application, toggle the ON/OFF switch at the top right:

Click Show to reveal your clientSecret:

Step 3 — Generate RSA Keys
Go to Merchant Portal → Developer → Application → Generate RSA Key. Recommended key size: 2048-bit.
- Private Key — store securely, never expose publicly. Used to sign your API requests.
- Public Key — upload to the Merchant Portal so RM can verify your signatures.

Step 4 — Wrap Your Public Key (For Signature Debugger)
Your public key needs to be wrapped in standard PEM format before using with the Signature Debugger:

Step 5 — Use the Signature Debugger (Optional)
For security purposes, RM has enhanced its authentication flow by adding layers of encryption to endpoints. You may develop your own encryption tool, or use the Signature Debugger to sign and verify content using your private/public keys:

Troubleshooting — INVALID_REQUEST_SIGNATURE
If you receive an INVALID_REQUEST_SIGNATURE error, the response includes a debug object showing exactly what the server computed at each step.
Compare the preVerifyContent steps below with your own output to find where they diverge.
1{2 "debug": {3 "preVerifyContent": {4 "step1": {5 "content": "{\"layoutVersion\":\"v2\",\"method\":[\"GOBIZ_MY\"],\"notifyUrl\":\"https://dev-rm-api.ap.ngrok.io\",\"order\":{\"additionalData\":\"world\",\"amount\":10,\"currencyType\":\"MYR\",\"detail\":\"hello\",\"id\":\"721115\",\"title\":\"hello\"},\"redirectUrl\":\"https://revenuemonster.my\",\"storeId\":\"10946114768247530\",\"type\":\"WEB_PAYMENT\"}",6 "remark": "Sort the json key alphabetically"7 },8 "step2": {9 "content": "eyJsYXlvdXRWZXJzaW9uIjoidjIiLCJtZXRob2QiOlsiR09CSVpfTVkiXSwibm90aWZ5VXJsIjoiaHR0cHM6Ly9kZXYtcm0tYXBpLmFwLm5ncm9rLmlvIiwib3JkZXIiOnsiYWRkaXRpb25hbERhdGEiOiJ3b3JsZCIsImFtb3VudCI6MTAsImN1cnJlbmN5VHlwZSI6Ik1ZUiIsImRldGFpbCI6ImhlbGxvIiwiaWQiOiI3MjExMTUiLCJ0aXRsZSI6ImhlbGxvIn0sInJlZGlyZWN0VXJsIjoiaHR0cHM6Ly9yZXZlbnVlbW9uc3Rlci5teSIsInN0b3JlSWQiOiIxMDk0NjExNDc2ODI0NzUzMCIsInR5cGUiOiJXRUJfUEFZTUVOVCJ9",10 "remark": "Encode the data using Base64 format"11 },12 "step3": {13 "content": "data=eyJsYXlvd...&method=post&nonceStr=XAYZRZNLGCKSTURRFKBIGYALUKLCLJOG&requestUrl=https://sb-open.revenuemonster.my/v3/payment/online&signType=sha256×tamp=1599467903",14 "remark": "Construct plain text parameters on this format. If the body is empty, the data parameter can be omitted."15 },16 "step4": {17 "content": "data=eyJsYXlvd...&method=post&nonceStr=XAYZRZNLGCKSTURRFKBIGYALUKLCLJOG&requestUrl=https://sb-open.revenuemonster.my/v3/payment/online&signType=sha256×tamp=1599467903",18 "remark": "Sign this content using sha256 with rsa private key and make sure the public key has been uploaded to the portal."19 },20 "step5": {21 "remark": "Pass the generated signature in the X-Signature header, prefixed with the sign type. Example: sha256 {{ signatureContent }}"22 }23 },24 "requestHeader": {25 "X-Nonce-Str": {26 "currentValue": "XAYZRZNLGCKSTURRFKBIGYALUKLCLJOG",27 "isValid": true,28 "remark": "The nonce string must not contain spaces and must be unique for at least 120 seconds."29 },30 "X-Signature": {31 "currentValue": "sha256 XvedDW8H2gqGL5gMzTHqDy1PXX3OqRF09WuQDkeCDwuinOAsPstcPOSefUwkyHPM9WPNKKHyR5qXbKNLC7UgQyGi8Ynio03kDo0p+g3BqXaUT1tpo5D8kv42Kh2S8CW4RkX2Dkf+Yxi2XMQ8l3kzPZaRyhudaGerUZony4Npzf63p4+oTBbXE01uX/4x/WL57+zkaaVRc1KlJsLdGsBmLlPOHLana7udJffJyxXhOmyokBuJ4GoOC8JpDG9oaKCNMZ88ow9CWWB0yRPrK2KeaEDwzCm2Jh8IFKw1gS6avQAwsjychZWv5XmAXkZ8ZQrnLXJquA09QpLxPTtOeQC9SA==",32 "isValid": false,33 "remark": "The signature is invalid."34 },35 "X-Timestamp": {36 "currentValue": "1599467903",37 "isValid": false,38 "remark": "The timestamp must be in UTC and within 120 seconds of the server time."39 }40 }41 },42 "error": {43 "code": "INVALID_REQUEST_SIGNATURE",44 "message": "The request signature is invalid"45 }46}
Common Causes
| Cause | Fix |
|---|---|
| Private key doesn't match the public key uploaded to the Merchant Portal | Re-upload the correct public key |
| JSON keys not sorted alphabetically | Check nested objects too |
| JSON body still contains spaces or newlines before Base64 encoding | Make the JSON compact |
Special characters (<, >, &) not replaced before encoding | Replace with \u003c, \u003e, \u0026 |
X-Timestamp is more than 120 seconds away from server UTC time | Sync your system clock |
X-Nonce-Str was reused within a 120-second window | Generate a new nonce for each request |
Amount is in cents — "amount": 100 = RM 1.00 | Multiply the amount by 100 |
{% endraw %}