Signing requests for SCA compliance
As you’re using a Volt Account, you need to sign each payout request using a private key, to comply with Strong Customer Authentication guidelines, in order for your payment to be straight-through-processed (ie with no further interaction required).
Note, this is in addition to the Authentication header you provide containing your credentials.
Why do you need to do this?
The Payment Services Directive (PSD2) requires that all payments are authenticated using two factors out of the following three:
- Something you know
- Something you have
- Something you are
How do you do this using the API?
We use the standard username and password authentication (as the “something you know” factor) and a private key to sign each request (the “something you have” factor).
We use JWS (JSON Web Signature) as the signature method for each request.
What happens if you don't sign each API request?
Your request won’t be processed for payment automatically, but will instead appear in a queue for processing in Fuzebox. You will need to login to Fuzebox and authorise the request using a passkey stored on your computer or mobile device. This can be done in bulk or for each individual payment.
Where can you test this?
You should test this on our sandbox environment as part of your integration test plan. We also recommend that you conduct penny tests (using small amounts) on the production environment for the entire payout process before your full “go-live” date.
Getting started
The first thing you’ll need to do is create a pair of private / public keys and share the public one with Volt. Then, for each request, create a JWT (JSON Web Token) which includes the signature and include that in the request header.
Create private / public keys
Technical specifications
- We support public keys in the following formats: PKCS1, PKCS8 and x509.
- The public key length should be between 2048 and 4096 bytes.
How to generate your keys
You can generate your private / public key pair using a terminal which supports the openssl
command, and by following the appropriate instructions below.
- X509
- PKCS1
- PKCS8
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 3650 -nodes -subj "/C=XX/ST=StateName/L=CityName/O=CompanyName/OU=CompanySectionName/CN=CommonNameOrHostname"
openssl genrsa -f4 -out private.txt 4096
openssl rsa -in private.txt -outform PEM -pubout -out public.txt
openssl genrsa -f4 -out private.txt 4096
openssl pkcs8 -topk8 -inform pem -in private.txt -outform PEM -nocrypt -out private8.txt
openssl rsa -in private8.txt -outform PEM -pubout -out public.txt
Send your public key to Volt
Please send the public key by email to support@volt.io.
Once you send the public key to us, we will email you an ID, a UUID which you’ll need to use when generating your signed token on each refund or payout request, so please keep this ID safe.
Example of an ID is 901659f9-c0fd-4d2e-82b8-a55f51f80d73
You should never share your private key with Volt, or with any third party. If you believe your private key may be compromised, you should immediately inform Volt, generate new a key pair and send us the new public key.
Create a JWT token for each request
To sign each request, you will need to provide a JWT token in the header of that request. To generate a signed JWT token, you’ll need your private key and the ID of your public key, plus the payload (the JSON body) of the POST request for your refund or payout.
Creating your JWT token
You need to create two parts for your JWT token, the header and the signature. The header tells us how to process the JWT token and the signature is the part that we’ll validate against the public key.
The token header segment
The header should be created first as a JSON object as below, where
alg
– represents the algorithm used to encode/decode the token, in this case it’sRS256
(the Rsa+Sha256 algorithm)typ
– should be “JWT
“kid
– theid
of the public key that we supplied you, which will be in UUID form
{
"alg": "RS256",
"typ": "JWT",
"kid": "ce161c49-4373-4b07-82fa-217998f6b3e8"
}
Encoding the header
Once you’ve created the JSON object as per the example above, you should remove any extra spaces or line breaks, then encode it using the base64UrlEncoded algorithm, as per the example below.
tokenHeader = '{"alg": "RS256","typ":"JWT","kid":"ce161c49-4373-4b07-82fa-217998f6b3e8"}'
encodedHeader = base64UrlEncode(tokenHeader)
… which should then look like the example below.
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImNlMTYxYzQ5LTQzNzMtNGIwNy04MmZhLTIxNzk5OGY2YjNlOCJ9
The signature segment
To create the signature segment, we need to use the algorithm that is specified in the header and the private key. This is where you’ll need a copy of the payload you’re sending in the POST request, because that’s what you’re actually signing here.
In this case we’re using the RSA+SHA256 algorithm and would use the sample code below, utilising the already encoded header plus a base64UrlEncoded version of the JSON payload you will be sending in the refund or payout request. You should not change the payload after generating the signature as this will invalidate the signed token.
Remove whitespace to improve validation
Please remove any line breaks or extra spaces in the request payload before using the base64UrlEncode function, this will ensure that your signature matches the way we’ll validate it. For example, in the case of refunds, you should base64UrlEncode('{"amount":1,"externalReference":"my-external-reference"}')
tokenHeader = '{"alg": "RS256","typ":"JWT","kid":"ce161c49-4373-4b07-82fa-217998f6b3e8"}'
requestPayload = '{"amount":1,"externalReference":"my-external-reference"}'
signature = RSASHA256( base64UrlEncode(tokenHeader) + "." + base64UrlEncode(requestPayload), privateKey )
If all is good you should get a signature which looks like this…
oUjLLtQigBniTiYswfE0JAjMiYXtIlNtVi1Lr1jqBx103vXgVtEdWApUMpG3wze3qVXD_APA2Sk8oLV4DGeBb5pN7yRGeCdxoV3IsikCVs6rn2Q2Jat-bReQMX39F7-Rpn7RznjHUsyWWcNbDKy1wRFcEnDJBVdb_1lKdFBPWaKMkB1Yd8t8X2va6mq7pJXPAMS36Gwc37vULZvdw4D-49r8mcbEGnNXwkcuZ08hMk4UsmM0kxLeNcrVD3wZtuU0N43u1trlPnuX9RDOOh9Gz0fEH1fwxdveAZaMOOWr7IPHBeV8nZXHxt1lpwJ-dpAsSDMvCFhr-MHuOQDDdqsfhQ
Build your JWT token
To build the final JWT token to sign your request, take the encoded header and join it to the signature as follows.
JWT = base64UrlEncode(header) + ".." + signature // Note the ".."
Important note
Please note the double dot ".."
is important when building the JWT token. Validation will fail if you miss one of the dots.
So, using the examples given in this page, your final token should look like the example below:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImNlMTYxYzQ5LTQzNzMtNGIwNy04MmZhLTIxNzk5OGY2YjNlOCJ9..oUjLLtQigBniTiYswfE0JAjMiYXtIlNtVi1Lr1jqBx103vXgVtEdWApUMpG3wze3qVXD_APA2Sk8oLV4DGeBb5pN7yRGeCdxoV3IsikCVs6rn2Q2Jat-bReQMX39F7-Rpn7RznjHUsyWWcNbDKy1wRFcEnDJBVdb_1lKdFBPWaKMkB1Yd8t8X2va6mq7pJXPAMS36Gwc37vULZvdw4D-49r8mcbEGnNXwkcuZ08hMk4UsmM0kxLeNcrVD3wZtuU0N43u1trlPnuX9RDOOh9Gz0fEH1fwxdveAZaMOOWr7IPHBeV8nZXHxt1lpwJ-dpAsSDMvCFhr-MHuOQDDdqsfhQ
Sign your request
Now you have your request payload and a JWT token which signs that payload, all you need to do is add a JWS header to each request, which contains the JWT token you created.
Header name : X-JWS-Signature
X-JWS-Signature: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImNlMTYxYzQ5LTQzNzMtNGIwNy04MmZhLTIxNzk5OGY2YjNlOCJ9..oUjLLtQigBniTiYswfE0JAjMiYXtIlNtVi1Lr1jqBx103vXgVtEdWApUMpG3wze3qVXD_APA2Sk8oLV4DGeBb5pN7yRGeCdxoV3IsikCVs6rn2Q2Jat-bReQMX39F7-Rpn7RznjHUsyWWcNbDKy1wRFcEnDJBVdb_1lKdFBPWaKMkB1Yd8t8X2va6mq7pJXPAMS36Gwc37vULZvdw4D-49r8mcbEGnNXwkcuZ08hMk4UsmM0kxLeNcrVD3wZtuU0N43u1trlPnuX9RDOOh9Gz0fEH1fwxdveAZaMOOWr7IPHBeV8nZXHxt1lpwJ-dpAsSDMvCFhr-MHuOQDDdqsfhQ