JWTs
The API uses Bearer (Token) Authentication to authenticate any request. These tokens are JSON Web Tokens (JWT) which need to be created server side by your application. By far the easiest way to create a JWT is with one of our server-side SDKs but it is also possible to generate the JWT without our SDKs using any number of open source JWT libraries.
This document defines our API specification for authentication, JWT claims, and signature mechanisms.
Authenticate an API call
Every API endpoint that requires authentication expects an Authorization HTTP header with a signed JWT token as its value (prefixed with Bearer). Payment APIs also require the x-merchant-account-id header.
curl -i -X GET "https://api.efundpay.com/v4/transactions?limit=20" \
-H "Authorization: Bearer <jwt>" \
-H "x-merchant-account-id: 202507131000347001"
Create an API secret
To sign a JWT you will need an API secret. In the EFundPay dashboard, the API secret is the merchant RSA private key generated from Developer Tools > API Credentials.
To create or rotate the API secret, open Developer Tools > API Credentials in your dashboard and click Generate RSA Credentials. Store the API secret with your server code or in a secure environment accessible to your application.
Merchants access
The x-merchant-account-id header identifies the merchant account for the request. If your dashboard account can access multiple merchant accounts, use the merchant account id for the account you are integrating.
Permissions
API access can be reduced by setting a restrictive set of scopes on the JWT.
Algorithm
Payment APIs use RSA-signed JWTs. Sign JWTs with the merchant RSA private key and set alg to RS512.
Generate JWT
You might not want to use one of our SDKs, or an SDK in your language might not be available. In those cases you can construct, and sign the JWT with one of the many libraries available on jwt.io.
At the high level a JWT is build up out of 3 pieces:
- A header defining the algorithm and key used to create the JWT.
- A set of claims that define the token's scope and other permissions.
- A cryptographic signature based on the header and the claims, signed using your private key.
Combine, these 3 pieces make up the JSON Web Token (JWT). See jwt.io for more details on the specification and available libraries for generating JWTs.
JWT header
The JWT header defines the signing algorithm and the site merchant account.
{
"typ": "JWT",
"alg": "RS512",
"kid": "sk_live_{site_merchant_id}"
}
The typ and alg are fixed. The kid identifies the site merchant account. In production, use sk_live_{site_merchant_id}. The site merchant id can be found in Site Management > Site List in the dashboard.
JWT claims
The claims define when the token was created and what access it has.
{
"iss": "My JWT Generation Tool",
"nbf": 1607976645,
"exp": 1607977245,
"jti": "0fe1fb1b-2f7e-4c8d-b0eb-aae5d0ec98f7",
"scopes": ["transactions.read"],
"merchantId": "202507131000347001",
"embed": {
"amount": "200",
"currency": "AUD",
"buyer_id": "d757c76a-cbd7-4b56-95a3-40125b51b29c",
"metadata": { "key": "value" },
"cart_items": [
{
"name": "Joust Duffle Bag",
"quantity": "1",
"unit_amount": "9000",
"tax_amount": "0"
}
]
}
}
Claims
The API supports the following JWT claims.
| Field | Description | Required |
|---|---|---|
| iss | A unique ID that represents your code making this call. This helps identify what library made an API call to EFundFlow. | Yes |
| nbf | The UNIX timestamp (in seconds) that this token was created at. | Yes |
| exp | The UNIX timestamp (in seconds) that this token expires at. | Yes |
| iat | An optional UNIX timestamp (in seconds) for your internal use to indicate when the token was issued. | No |
| jti | A random unique ID used for cryptographic entropy. This needs to be unique for each JWT. | Yes |
| scopes | A list of scopes that give this token access to the API. | Yes |
| embed | A dictionary of key-value pairs used to pin the amount, currency, and buyer info for use in Embed. | No |
| checkout_session_id | The ID of a checkout session. This can be used to tie multiple transactions together as having originated from the same session. | No |
| merchantId | The merchant unique identifier. If present, it must match the x-merchant-account-id header. | No |
Timestamps
Please be aware that the nbf, exp, and iat values are UNIX timestamps defined as seconds since January 1st, 1970 (UTC). Some programming languages will return UNIX timestamps as milliseconds, requiring the removal of the last 3 digits.
Scopes
The API supports the following values for the scopes claims.
| Scope | Description |
|---|---|
| *.read | Allows read-access to any resource. This is used by default in the SDKs |
| *.write | Allows write-access to any resource. This is used by default in the SDKs. This does not also allow read access. |
{resource_name}.read | Allows read-access to a type or resource. For example, payment-services.read enabled read-access for buyers data. |
{resource_name}.write | Allows write-access to a type or resource. For example, payment-services.write enabled write-access for buyers data. This does not also allow read access. |
| embed | A scope that represents all the access needed by Embed. |
The following resource names are recognized. Please see the reference documentation for more details as to what scope is required per endpoint.
anti-fraud-servicesapi-logsbuyersbuyers.billing-detailscard-scheme-definitionscheckout-sessionsconnectionsdigital-walletsflowspayment-methodspayment-method-definitionspayment-optionspayment-service-definitionspayment-servicesreportstransactions
Signature & assembly
Finally, the JWT signature is generated by appending the Base64 encoded header and claims (separated with a .) and signing that value with the merchant RSA private key.
RSA
SHA512withRSA(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
private_key
)
The assembled JWT is then formed by appending the Base64 encoded header, claims, and signature separated by a full stop.
base64UrlEncode(header) + "." + base64UrlEncode(payload) + "." + base64UrlEncode(signature)
Code Example
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.Instant;
import java.util.Base64;
import java.util.UUID;
public class JwtExample {
static String createJwt(String privateKeyBase64, String merchantId, String siteMerchantId) throws Exception {
long now = Instant.now().getEpochSecond();
String header = "{\"typ\":\"JWT\",\"alg\":\"RS512\",\"kid\":\"sk_live_" + siteMerchantId + "\"}";
String payload = "{"
+ "\"iss\":\"efundflow.com\","
+ "\"nbf\":" + (now - 60) + ","
+ "\"exp\":" + (now + 3600) + ","
+ "\"jti\":\"" + UUID.randomUUID() + "\","
+ "\"scopes\":[\"transactions.read\"],"
+ "\"merchantId\":\"" + merchantId + "\""
+ "}";
String encodedHeader = base64UrlEncode(header.getBytes(StandardCharsets.UTF_8));
String encodedPayload = base64UrlEncode(payload.getBytes(StandardCharsets.UTF_8));
String signingInput = encodedHeader + "." + encodedPayload;
Signature signature = Signature.getInstance("SHA512withRSA");
signature.initSign(loadRsaPrivateKey(privateKeyBase64));
signature.update(signingInput.getBytes(StandardCharsets.UTF_8));
return signingInput + "." + base64UrlEncode(signature.sign());
}
static PrivateKey loadRsaPrivateKey(String base64Key) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(base64Key);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
return KeyFactory.getInstance("RSA").generatePrivate(keySpec);
}
static String base64UrlEncode(byte[] bytes) {
return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
}
}