Skip to main content

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

orderObjectrequired

Object of order

Example: (Refer to explanation below)

customerObjectrequired

Object of customer

Example: (Refer to explanation below)

method[]Stringrequired

RM currently supported method

Example: []

typeObjectrequired

Object of type. See Type Object below.

Example: (Refer to explanation below)

storeIdStringrequired

ID of the store to create QR code

Example: "10946114768247530"

redirectUrlStringrequired

URL to redirect after payment is made

Example: "https://google.com"

notifyUrlStringrequired

Webhook URL that RM will call with the payment result

Example: "https://google.com"

layoutVersionString

Select layout for Web payment. v1 / v2 (Credit Card) / v3 (Credit Card and FPX)

Example: "v3"

Order object (order):

titleStringrequired

Order title. Max 32 characters.

Example: "Sales"

detailStringrequired

Order detail. Max 600 characters.

Example: "1 x iPhone X; 2 x SAMSUNG S8"

additionalDataStringrequired

Additional order description.

Example: "Sales"

amountUintrequired

Amount of order in cents. Minimum is RM 0.10 (amount: 10). Required only when isPrefillAmount = true.

Example: 100

currencyTypeStringrequired

Currency notation. Currently only MYR is supported.

Example: "MYR"

idString

Your internal order reference ID.

Example: "6170506694335521334"

Customer object (customer):

userIdStringrequired

Required if tokenization is enabled.

Example: "13245876"

emailString

Customer email address.

Example: ""

countryCodeString

Customer country code.

Example: ""

phoneNumberString

Customer phone number.

Example: ""

Type object (type):

typeStringrequired

Use WEB_PAYMENT for browser-based payments.

Example: "WEB_PAYMENT"

typeStringrequired

Use MOBILE_PAYMENT for mobile app payments.

Example: "MOBILE_PAYMENT"

Example Request

Example Request
JSON
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:

  1. Sort all JSON keys alphabetically — including nested objects
  2. Make the JSON compact — remove extra spaces and newlines
  3. Replace special characters:
CharacterReplace With
<\u003c
>\u003e
&\u0026

Step 2 — Base64-encode the Data

Take the compact, sorted JSON string and Base64-encode it.

Base64 Encoded
Text
ewogICAgIm9yZGVyIjogewogICAgCSJ0aXRsZSI6ICJoZWxsbyIsCiAgICAJImRldGFpbCI6IC
IiLAogICAgCSJhZGRpdGlvbmFsRGF0YSI6ICJ3b3JsZCIsCgkgICAgImFtb3VudCI6IDEwLAoJIC
AgICJjdXJyZW5jeVR5cGUiOiAiTVlSIiwKCSAgICAiaWQiOiAgIjcyMTEiCiAgICB9LAogICAgIm
N1c3RvbWVyIjogewogICAgInVzZXJJZCI6ICIiLAogICAgImVtYWlsIjogIiIKfSwKICAgICJtZX
Rob2QiOltdLAogICAgInR5cGUiOiAiV0VCX1BBWU1FTlQiLAogICAgInN0b3JlSWQiOiAiMTYwOD
EyMzAzNTU2NDUzODEyMSIsCiAgICAicmVkaXJlY3RVcmwiOiAiaHR0cHM6Ly9yZXZlbnVlbW9uc3
Rlci5teSIsCiAgICAibm90aWZ5VXJsIjogImh0dHBzOi8vZGV2LXJtLWFwaS5hcC5uZ3Jvay5pby
IsCiAgICAibGF5b3V0VmVyc2lvbiI6InYzIgp9

Step 3 — Construct the Signing String

  • If the body is empty, the data parameter can be skipped.
  • If you are verifying a callback signature, the requestUrl can be skipped.
dataStringrequired

Base64-encoded data body from Step 2.

Example: (See Step 2 output)

methodStringrequired

HTTP call method used.

Example: "post"

nonceStrStringrequired

A unique random string. Must not be reused within 120 seconds.

Example: "VYNknZohxwicZMaWbNdBKUrnrxDtaRhN"

requestUrlStringrequired

The exact API URL being called, including the full path.

Example: "https://sb-open.revenuemonster.my/v3/payment/online"

signTypeStringrequired

The signing algorithm to use.

Example: "sha256"

timestampStringrequired

UNIX timestamp of the request. Must be within 120 seconds of server time.

Example: "1527407052"

Signing String
Text
data=ewogICAgIm9yZGVyIjog...&method=post&nonceStr=VYNknZohxwicZMaWbNdB
KUrnrxDtaRhN&requestUrl=https://sb-open.revenuemonster.my/v3/payment/online&signType=sha256&timestamp=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.

Signature
Text
sha256 IrBg6t73VsH7ieEnQDB4CXHFjMWUkp8Dtddpxqw+4Gvz6Tag7Dx6nrfAt2ofYK8xZN9aBCvAKAfmAOGWIXnsTXfhFBnMA2kadiga7ufUJ81ozyhllbiliRM2ugw1OcqSTLRHWBPhrVwhHBxgDiG9wbuI3FKURrz+CufYYakFoCw=

Step 5 — Add Headers to Your Request

Place the signature in the X-Signature header, prefixed with the sign type:

HeaderValue
X-Signaturesha256 <your_signature>
X-Nonce-StrThe nonce string you generated
X-TimestampThe Unix timestamp you generated
AuthorizationBearer <access_token>
Content-Typeapplication/json

Example cURL Request

cURL
Bash
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

itemObject

Response payload. See Item object below.

Example: (Refer to explanation below)

codeString

SUCCESS if the call succeeded. Otherwise returns an error code object. See Appendix: Error Codes.

Example: "SUCCESS"

Item object (item):

checkoutIdString

Code to identify the web payment session.

Example: "1548316308361173347"

urlString

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

Response
JSON
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.

Debug Response
JSON
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&timestamp=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&timestamp=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

CauseFix
Private key doesn't match the public key uploaded to the Merchant PortalRe-upload the correct public key
JSON keys not sorted alphabeticallyCheck nested objects too
JSON body still contains spaces or newlines before Base64 encodingMake the JSON compact
Special characters (<, >, &) not replaced before encodingReplace with \u003c, \u003e, \u0026
X-Timestamp is more than 120 seconds away from server UTC timeSync your system clock
X-Nonce-Str was reused within a 120-second windowGenerate a new nonce for each request
Amount is in cents — "amount": 100 = RM 1.00Multiply the amount by 100

{% endraw %}