Merchant Account at RelayPay
This is a guide for merchants integrating with RelayPay as a payment processor.
Before starting you need to visit the Merchant Dashboard found at https://app.sandbox.relaypay.io and create an account.
This is the admin dashboard you can use to see transactions and update any settings associated with your account.
When you complete you profile we’ll need to activate your account. Please reach out to us to complete KYB.
Once know-you-business verification is complete you will be able to generate private
and public
keys in the UI to be used for API requests.
Sandbox environment
The API Base url for the sandbox environment is api.sandbox.relaypay.io
Sandbox environment is intended for development of an integration and testing purposes. Transactions on sandbox do not require real money nor crypto currency to test. See testing.
Production environment
API base url is for the production environment is api.relaypay.io
In order to use the production environment, as mentioned earlier, you’ll need to open an account at https://app.relaypay.io
Overview of Flow
Authentication
All our rest endpoints are secure and any caller must provide authentication to the request. Al requests must be standard HTTPS protocol. plain HTTP is not supported.
Common headers
- the request must have a header name
x-api-key
with public api key as value. - the request must have a header name
x-merchant-id
with your merchantId (obtained when creating the account).
POST and PUT requests
- the request body must be application/json
- the request must have a header name
x-api-signature
with calculated value based on the json payload.
x-api-signature header
Authenticated requests should be signed with x-api-signature header, using a signature generated with SHA 256 of stringified JSON payload and private key according to:
SHA256(payload + privateKey)
Example Sign
Below is a specific example of a sign generated with a particular stringified JSON payload and private key. The request payload used for generating sign and sending API request should be identical to avoid any issue because same payload with different whitespace can generate entirely different signature. Code snippets for generating the sign in Node.js, Python and Java are shown below:
Field | Value |
---|---|
Private (Secret) Key | DOSsQcRP9NnzFJxVQL4W |
Payload | {"orderId":"12345","transactionId":"advgjq-1ba7ak-asdhja8","orderStatus":"Success"} |
Concat the json String and secret key String | {"orderId":"12345","transactionId":"advgjq-1ba7ak-asdhja8","orderStatus":"Success"}DOSsQcRP9NnzFJxVQL4W |
The UTF-8 String converted to byte array | [123, 34, 111, 114, 100, 101, 114, 73, 100, 34, 58, 34, 49, 50, 51, 52, 53, 34, 44, 34, 116, 114, 97, 110, 115, 97, 99, 116, 105, 111, 110, 73, 100, 34, 58, 34, 97, 100, 118, 103, 106, 113, 45, 49, 98, 97, 55, 97, 107, 45, 97, 115, 100, 104, 106, 97, 56, 34, 44, 34, 111, 114, 100, 101, 114, 83, 116, 97, 116, 117, 115, 34, 58, 34, 83, 117, 99, 99, 101, 115, 115, 34, 125, 68, 79, 83, 115, 81, 99, 82, 80, 57, 78, 110, 122, 70, 74, 120, 86, 81, 76, 52, 87] |
Calculate the SHA-256 digest from the byte array and return the value as a byte array. | [106, -64, 126, -38, -12, -28, -85, -9, -84, -94, -116, 6, -67, 38, 32, -67, 35, 35, 42, 111, 76, 90, 44, 79, -51, 50, -118, -25, 42, -66, -8, -2] |
Convert an array of bytes into an array of characters representing the hexadecimal values of each byte in order in lower case. That's your Sign value | 6ac07edaf4e4abf7aca28c06bd2620bd23232a6f4c5a2c4fcd328ae72abef8fe |
Code Examples
Node.js
const crypto = require('crypto');
const getSign = (payload, privateKey) => {
const hash = new crypto.createHash('sha256');
return hash.update(payload + privateKey).digest('hex');
};
console.log(getSign('{"orderId":"12345","transactionId":"advgjq-1ba7ak-asdhja8","orderStatus":"Success"}', "DOSsQcRP9NnzFJxVQL4W"));
Python
import hashlib
def get_sign(payload, privateKey):
valueToHash = payload + privateKey
return hashlib.sha256(valueToHash.encode()).hexdigest()
print(get_sign('{"orderId": "12345", "transactionId": "advgjq-1ba7ak-asdhja8", "orderStatus": "Success"}', 'DOSsQcRP9NnzFJxVQL4W'));
Java
public String getSign(String payload, String privateKey) {
// Use org.apache.commons.codec.digest.DigestUtils
return DigestUtils.sha256Hex(payload + privateKey);
}
System.out.println(getSign("{\"orderId\": \"12345\", \"transactionId\": \"advgjq-1ba7ak-asdhja8\", \"orderStatus\": \"Success\"}", "DOSsQcRP9NnzFJxVQL4W"));
OpenAPI
OpenAPI is available here.
Create transaction
Request
Post request to:
/api/v1/ecommerce/request
Requests should be signed.
Parameter | Required | Example | Description |
---|---|---|---|
amount | yes | 42.87 | |
customerName | yes | Jacob | |
customerEmail | yes | [email protected] | |
storeName | yes | Store24 | |
merchantId | yes | [email protected] | merchant email (login) |
currency | yes | AUD | This is the fiat currency we settle in with you. |
orderId | yes | 12345 | Id generated on the merchant side. Will be returned in webhook payload. |
callbackUrlRedirect | no | <https://yourwebsite.example.com/transaction/status > | Where to redirect once the payment is pending (or cancelled if callbackCancelUrlRedirect is blank). If this value is provided, it will override the fallback option provided in the general settings accessed through the merchant dashboard. |
callbackCancelUrlRedirect | no | <https://yourwebsite.example.com/cancel > | Where to redirect once the payment is cancelled. Defaults to callbackUrlRedirect if not provided |
webHookUrl | no | <https://yourwebsite.example.com/api/tx-update > | Where to post updates about a payment. These will be posted with every payment change. This callback is guaranteed to happen before we redirect the user back. If this value is provided, it will override the fallback option provided in the general settings accessed through the merchant dashboard. Setting this value in the general settings is also optional. If neither place is filled in then the webhook will not be sent. |
securityToken | no | Basic dXNlcjpwYXNzd29yZA== | This token will be sent back with the callback in Authorization header. (Authorization: [type] [credentials]) When creating the token, both 'type' and 'credentials' are required. |
Header Name | Required | Example | Description |
---|---|---|---|
x-api-signature | yes | 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a | SHA 256 of stringify payload + private key |
Response
Status 200: redirect url
Status 401: Unauthorised Request
Check your public and private keys, check signing process.
Transactions status updates
Webhook
Webhook will be send to webHookUrl
which was provided in the create transaction request step.
Webhook x-api-signature
header should be verified on the merchant side with private key.
Webhook Authorization
header should be verified on the merchant side (if used).
Webhook will be sent once for every status update.
Merchant should respond with status OK (200). If RelayPay does not receive status OK - it will retry sending the webhook every 1 minute until status OK is received for a total of 5 retries.
Webhook payload example:
{
"orderId": "12345",
"transactionId": "advgjq-1ba7ak-asdhja8",
"orderStatus": "Success",
"customerEmail": "[email protected]",
"customerName": "John Doe",
"amount": 42.05
}
Header x-api-signature
= dbda6873028004858d93d93bc4c7e751019cf4aad0577922d119ed69f5553650
Header Authorisation
= Basic dXNlcjpwYXNzd29yZA==
Possible statuses:
Cancelled
- cancelled by customerSuccess
- transaction successfully completedFailed
- transaction failedPending
- transaction not completed yet.Expired
- transaction not completed yet.
Get transactions list
/api/v1/merchant/transaction/history
Method: GET
Params:
- page - number of page
- size - amount of the elements on the page
Response example:
{
"totalPages": 0,
"totalElements": 0,
"size": 0,
"content": [
{
"orderId": "12345",
"transactionId": "advgjq-1ba7ak-asdhja8",
"orderStatus": "Cancelled",
"customerEmail": "[email protected]",
"customerName": "John Doe",
"currency": "AUD",
"amount": 42.05
}
],
"number": 0,
"sort": {
"unsorted": true,
"sorted": true,
"empty": true
},
"numberOfElements": 0,
"first": true,
"last": true,
"pageable": {
"offset": 0,
"sort": {
"unsorted": true,
"sorted": true,
"empty": true
},
"paged": true,
"unpaged": true,
"pageNumber": 0,
"pageSize": 0
},
"empty": true
}
Get merchant transaction
/api/v1/merchant/transaction
Method: GET
Params:
- orderId - unique merchant orderId
Response example:
{
"orderId": "12345",
"transactionId": "advgjq-1ba7ak-asdhja8",
"orderStatus": "Success",
"customerEmail": "[email protected]",
"customerName": "John Doe",
"currency": "AUD",
"amount": 42.05
}
Possible statuses:
Cancelled
- cancelled by customerSuccess
- transaction successfully completedFailed
- transaction failedPending
- transaction not completed yet.
public String getSign(String stringifiedPayload, String privateKey) {
// Use org.apache.commons.codec.digest.DigestUtils
return DigestUtils.sha256Hex(stringifiedPayload + privateKey);
}
System.out.println(getSign("{\"orderId\": \"12345\", \"transactionId\": \"advgjq-1ba7ak-asdhja8\", \"orderStatus\": \"Success\"}", "DOSsQcRP9NnzFJxVQL4W"));
Verify webhook
RelayPay will send you webhook call with status updates. Request contains header x-api-signature
.
To verify authenticity and be sure that request was send from RelayPay you should take stringified request’s payload, concatenate private key and take SHA-256 hash from it. Then just compare result with the value of Sign
header from request.
Another header sent is Authorization
if there was a securityToken provided with the transaction request.
Testing
By default you will receive Pending
and then Complete
transaction status.
For testing your backend, you can induce the following responses by sending the following customerEmail
on the first step:
CustomerEmail | Create tx response | Tx status |
---|---|---|
[email protected] | Status 200 | Cancelled |
[email protected] | Status 200 | Pending → Failed |
[email protected] | Status 200 | Pending → Expired |