Troubleshooting Signed REST Requests

Given the binary nature of signature verification (it passes or it doesn't), troubleshooting problems with signatures can be difficult. There is a broad range of input data and only a single boolean output. If a signed REST request fails then the problems causing this may include the following:

  • Signed request string does not match the request sent by your back end
  • Expiry time not within a valid range
  • Incorrect signing algorithm
  • Invalid request body
  • Incorrect private or public key

If you are having problems with signed REST requests we recommend starting with a basic request and building it one field at a time until it mirrors requests that your system will be sending. The zsh scripts below can be used for this. It sends a basic signed hosted verified payout request.

#!/bin/zsh
#
# Script to test request signatures for verified hosted payout REST requests.
#
# The script can be called as follows:
#
#   zsh ./get_hosted_payout_link.zsh
#
# The configurable variables below should be set before using script.

###### Configurable variables.

# Put your private API key here.
#
YOUR_MERCHANT_PRIVATE_API_KEY="5f219015-de07-10f1-529a-ba018baeff01"

# Put your RSA private key file path here.
#
YOUR_RSA_PRIVATE_KEY_FILE="./private_key.pem"

# Update this to reflect requests you will be sending.
#
REQUEST_BODY='{
    "amount": 1.05,
    "currency": "GBP",
    "paymentGiro": "FPS",
    "reference": "ref123",
    "customerIdentifier": "[email protected]",
    "customerIpAddress": "1.2.3.4",
    "customerDeviceOs": "Android"
}'

###### End Configurable variables.

ENDPOINT='https://testapi.yaspa.com/v2/payouts/hosted-payout/generate-link'

currentTime=$(date +%s)
signatureExpireTime=$((currentTime + 600))

signaturePlainText="$signatureExpireTime|POST|$ENDPOINT|$REQUEST_BODY"

encodedSignature=$(echo -n $signaturePlainText | openssl dgst -sha256 -sign $YOUR_RSA_PRIVATE_KEY_FILE | base64)

curl -X POST $ENDPOINT \
  -H "AuthorizationCitizen: $YOUR_MERCHANT_PRIVATE_API_KEY" \
  -H "Expires-at: $signatureExpireTime" \
  -H "Signature: $encodedSignature" \
  -H "Content-Type: application/json" \
  -d $REQUEST_BODY
#!/bin/zsh
#
# Script to test request signatures for verified hosted payout REST requests.
#
# The script can be called as follows:
#
#   zsh ./get_hosted_payout_link.zsh
#
# The configurable variables below should be set before using script.

###### Configurable variables.

# Put your private API key here.
#
YOUR_MERCHANT_PRIVATE_API_KEY="5f219015-de07-10f1-529a-ba018baeff01"

# Put your RSA private key file path here.
#
YOUR_RSA_PRIVATE_KEY_FILE="./private_key.pem"

# Update this to reflect requests you will be sending.
#
REQUEST_BODY='{
    "amount": 1.05,
    "currency": "EUR",
    "paymentGiro": "SEPA",
    "reference": "ref123",
    "customerIdentifier": "[email protected]",
    "customerIpAddress": "1.2.3.4",
    "customerDeviceOs": "Android"
}'

###### End Configurable variables.

ENDPOINT='https://testapi.yaspa.com/v2/payouts/hosted-payout/generate-link'

currentTime=$(date +%s)
signatureExpireTime=$((currentTime + 600))

signaturePlainText="$signatureExpireTime|POST|$ENDPOINT|$REQUEST_BODY"

encodedSignature=$(echo -n $signaturePlainText | openssl dgst -sha256 -sign $YOUR_RSA_PRIVATE_KEY_FILE | base64)

curl -X POST $ENDPOINT \
  -H "AuthorizationCitizen: $YOUR_MERCHANT_PRIVATE_API_KEY" \
  -H "Expires-at: $signatureExpireTime" \
  -H "Signature: $encodedSignature" \
  -H "Content-Type: application/json" \
  -d $REQUEST_BODY

The following steps should be followed for troubleshooting with the script:

  1. Ensure you have cURL and OpenSSL installed.
    • These can be installed with the following commands on a Mac:
      • brew install openssl
      • brew install curl
  2. Copy the shell script for GBP or EUR payouts as appropriate and save it to: get_hosted_payout_link.zsh. Then configure the script:
    • Set the variables: YOUR_MERCHANT_PRIVATE_API_KEY and YOUR_RSA_PRIVATE_KEY_FILE
  3. Run the script using the command: zsh ./get_hosted_payout_link.zsh
    • If the script does not run then because it cannot find OpenSSL or similar then you can paste the whole script then you may paste the script and error message into a LLM such as Claude or ChatGPT. Be careful about installing packages – the script should not need anything other than cURL and OpenSSL. Also, do not include any production keys.
    • If a 401 response is returned then there is likely a problem with either the private or public RSA key. Check that the private RSA key corresponds to the public RSA key registered. Also check that it is a 2048 bit key.
    • If a 403 response is returned then there is likely a problem with your merchant private API key. Check that this correct.
  4. Add fields in the REQUEST_BODY variable to match the fields you intend to set in payout requests. The fields should be added one at a time and then tested.
    • If a 400 response is returned then it is likely that a required field has been removed. Check the documentation for required fields for the REST call.
  5. Once the request sent by the script reflects one that will be sent by your system then try a signed request from it.
    • If a 400 response is returned then check if a required field is missing from the call from your system.
    • If a 403 response is returned then your system is likely using an incorrect merchant private API key. Check that this correct.
    • If a 401 response is returned then check the following:
      • Check if your system is including special chars in the request it's sending.
      • Check that the order of fields sent in the request matches that in the requestBody part of the signed string.
      • Check that your system is using the same private RSA key as the script.
      • Check that the expires-at header is set to a UNIX timestamp between now and 10 minutes from now. It should also match the value in the expiryTime part of the signed string.
      • Check that your system is using the correct signing algorithm: SHA256withRSA. In this algorithm a SHA 256 hash is taken of the string and the hash is then signed.

It should be possible to complete steps 1 to 4 without customer support. These steps should rule out the most obvious problems. If you receive a 401 response at step 5 we can help by checking logs at our end, which may be useful for the following issues:

  • Checking the request that your system is sending to see if special chars are being added or if fields are being re-ordered
  • Checking if the expires-at header is within the allowed range.
  • Checking for problems with key length or signing algorithm.