Set Up Custom Integration
This tutorial guides you through setting up a custom integration to the REST API. A custom integration is best for businesses that need to align payment processing with their own systems, processes, and customer experience requirements.
Sign Up for a Test Account
To begin setting up your account, you must first sign up for a sandbox test account. A test account enables you to obtain your REST API keys and test your implementation.
Follow these steps to sign up for a sandbox test account:
Go to the Developer Center test account sign-up page:
Enter your information into the test account form, and click Create Account.
Go to your email and find a message titled Merchant Registration Details and click Set up your username and password now.
Your browser opens the New User Sign Up wizard.
Enter the organization ID and contact email you submitted during account creation.
Follow the wizard pages and on-screen instructions to enter your name, username, and password.
Log in to the using your account credentials:
The Verify your Identity page appears. When you log in for the first time, you must verify your identity through a system-generated email sent to your registered email account.
Check your email inbox for a message titled Identification Code. A passcode is included in the message.
Enter the passcode on the Verify your Identity page.
You are directed to the dashboard page.
Choose Your REST API Key
Decide which REST API key is best for your organization and click its corresponding option below. REST API keys are cryptographic keys that determine how your system constructs REST API messages.
| Feature | P12 Certificate | Shared Secret Key Pair |
|---|---|---|
| Best for | Server-side integrations using certificate-based authentication | Most REST API integrations using HTTP Signature authentication |
| How it works | A .p12 certificate file and password authenticate each request | An API key ID and shared secret key sign each request |
| Key benefit | Strong certificate-based security | Easier to generate, store, and rotate |
Generate P12 Certificate
This task describes how to:
- Create or submit a P12 certificate.
- Extract the P12 certificate's private key.
- Test the private key to verify that it works.
A private key is necessary for you to construct JSON Web Tokens (JWTs).
Create or Submit a P12 Certificate
You can choose to create a new certificate or submit an existing P12 certificate that you created in your system.
Follow these steps to create a P12 certificate file or submit your own certificate signing request (CSR):
On the left navigation panel, choose Payment Configuration > Key Management.
Click + Generate key on the Key Management page.
Under REST APIs, choose REST – Certificate, and then click Generate key.
The Key Generation page appears.
If you are using a portfolio account, the Key options window appears, giving you the choice to create a meta key. For more information about how to create a meta key, see Meta Keys.
The Confirmation Key Generation window appears.
Click Download key after reviewing the key details.
The Key Generation page appears.
(Optional) You can set the Certificate Expiry Timeframe field to the number of months you want the key to remain active before it expires. Only whole numbers from 1–36 are accepted. By default, new keys expire after 12 months.
Choose from these two options:
If you are a creating a new P12 Certificate, click Download key .
If you are submitting your own certificate, enter your public PEM-formatted certificate in the text box, then click Download key .
Create a password for the certificate by entering one into the New Password and Confirm Password fields. Click Generate key.
To create or submit another key, click Generate another key. To view all of your created keys, go to the Key Management page.
Extracting the Private Key from Your P12 Certificate
When you have your P12 certificate, extract the private key from the certificate. Use this key to sign your header when sending an API request.
Follow these steps to extract the private key using OpenSSL:
- Open the command-line tool and navigate to the directory that contains the P12 certificate.
- Enter this command:
openssl pkcs12 -in [certificate name] -nodes -nocerts -out [private key name]Enter the password for the certificate.
You set this password when you created the P12 certificate in the .
Testing Your Private Key
After creating your key certificate, you must verify that it can successfully process API requests. This task explains how to test and validate your private key in the Developer Center and the .
Follow these steps:
Go to the API Reference page:
Under Authentication and Sandbox Credentials, go to the Authentication Type drop-down menu and choose JSON Web Token.
Enter your organization ID in the Organization field.
Enter your Password in the Password field.
Click Browse and upload your p12 certificate from your desktop.
Click Update Credentials.
A confirmation message verifies that your credentials are successfully updated.
On the left navigation panel, choose Payments > POST Process a Payment.
Click Send.
A message confirms that your request is successful with the status code 201.
Log in to the :
On the left navigation panel, choose Transaction Management > Transactions.
Under Search Results, verify that the request ID from the test authorization response is listed in the Request ID column.
Construct a Request Using a JWT
This task explains how to construct a JWT by setting and combining these required components:
- HTTP message header
- HTTP message body
Overview of JWT Construction
Set the Known HTTP Header Elements
The HTTP message header consists of the content-type, host, and authorization HTTP header elements.
- content-type
- host
- authorization
To be begin constructing the JWT, set the known header values:
| HTTP Header Element | Description |
|---|---|
| content-type | Set to the media or file type of the request, which is also known as the Multipurpose Internet Mail Extension (MIME) type. Example: application/json |
| host | Set to the endpoint host name. Example: |
| authorization | A JSON Web Signature (JWS) bearer token that you construct. Steps 3B – 3D describe how to construct token value. The token consists of three segments separated by the period character (.). For example: Bearer <Base64URL-encoded JWS header>.<Base64URL-encoded JWS body claims>.<Base64URL-encoded signature> |
Set the JWS Header Claims
This step begins the process of constructing a JSON Web Signature (JWS) token. To construct a JWS token, you must first create the JWS header by setting its header claim values. After creating the JWS header, use Base64URL to encode it. The encoded header claim value is the first segment of the JWS token.
These header claim values do not require calculation.
| Header Field | Description |
|---|---|
| alg | The asymmetric algorithm you use to sign the token header. These algorithms are supported: RS256 (default), RS384, RS512, PS256, PS384, and PS512. |
| kid | The key ID you use to digitally sign the JWT. It must be registered with the authorizing server. It is the key ID from your P12 certificate. For more information, see Create or Submit a P12 Certificate. |
| typ | The token type. Set to JWT. |
Set the JWS Body Claims
After you set the JWS header values, you must create the JWS body by setting these body claim values. After the body claims are created, use Base64URL to encode it. The encoded body claim value is the second segment of the JWS token.
| JWS Body Claim Field | Description | Data Type | Field Value Format |
|---|---|---|---|
digest | A Base64-encoded hash of the message payload. Do not include the digest field if the request message is empty, such as during a GET or DELETE request. | String | Base64-encoded string: uppercase, lowercase, digits, +, /, and optional = padding |
digestAlgorithm | The algorithm used to hash the message payload. The message payload should be hashed using the SHA-256 algorithm. Do not include the digestAlgorithm field if the digest field is not included. | String | Lowercase |
exp | The time at which the JWS token expires. Important: field values cannot exceed two minutes after the message issue date, which is the iat field value. This field is an HTTP-date value as defined in RFC 7231. For example, 01/01/2020 at 00:02:00 is 1577836920. | String | Numeric |
iat | The date and time at which the message is issued. This field uses a NumericDate value as defined in RFC 7519, which is the number of seconds since 1970-01-01T00:00:00Z (Unix epoch). For example, 01/01/2020 at 00:00:00 is 1577836800. | String | Numeric |
iss | The issuer identifier for the JWS token. Set to the merchant ID that created the P12 certificate. This value is used to validate the issuer. | String | Lowercase |
jti | The unique token ID. This value is used for replay prevention. Format the value using UUID version 4. For example, 6643fb9a-8093-47c6-95d3-8d69785b5e62. | String | Lowercase alphanumeric |
request-host | The endpoint hostname for the HTTP request, excluding the protocol and path. For example, to send a message to the /pts/v2/payments endpoint, set this field to . | String | Lowercase alphanumeric with periods |
request-method | The HTTP request method. For example, post, get, put, patch, or delete. | String | Lowercase |
request-resource-path | The endpoint path for the HTTP request, excluding the domain. For example, to send a message to the /pts/v2/payments endpoint, set this field to /pts/v2/payments. | String | Lowercase alphanumeric |
v-c-jwt-version | The Visa JWT scheme version number. Set to 2. | String | Numeric |
v-c-merchant-id | Your transacting merchant ID (MID). If you are a portfolio or merchant account user, set this to the transacting merchant ID you send requests on behalf of. | String | Lowercase alphanumeric |
v-c-response-mle-kid | The message-level encryption response key ID, also known as the REST-API Response MLE key. | String | Lowercase alphanumeric |
The value of the digest JWS claim is a hashed version of the HTTP message body that you must calculate. uses this hash value to validate the integrity of your message body.
Follow these steps to calculate the digest hash:
- Generate the SHA-256 hash of the JSON payload (message body).
- Encode the hashed string to Base64.
- Add the message body hash to the digest JWS body claims.
- Add the algorithm used to hash the digest in the digestAlgorithm JWS body claims.
echo -n "6ae5459bc8a7d6a4b203e8a734d6a616725134088e13261f5bbcefc1424fc956" | base64public static string GenerateDigest(){ var digest = ""; var bodyText = "{ your JSON payload }"; using (var sha256hash = SHA256.Create()) { byte[] payloadBytes = sha256hash.ComputeHash(Encoding.UTF8.GetBytes(bodyText)); digest = Convert.ToBase64String(payloadBytes); } return digest;}public static String GenerateDigest() throws NoSuchAlgorithmException { String bodyText = "{ your JSON payload }"; MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update(bodyText.getBytes(StandardCharsets.UTF_8)); byte[] digest = md.digest(); return Base64.getEncoder().encodeToString(digest);}cat <<'EOF' | tr -d '\n' | shasum -a 256{ "clientReferenceInformation": { "code": "TC50171_3" }, "paymentInformation": { "card": { "number": "4111111111111111", "expirationMonth": "12", "expirationYear": "2031" } }, "orderInformation": { "amountDetails": { "totalAmount": "102.21", "currency": "USD" }, "billTo": { "firstName": "John", "lastName": "Doe", "address1": "1MarketSt", "locality": "sanfrancisco", "administrativeArea": "CA", "postalCode": "94105", "country": "US", "email": "", "phoneNumber": "4158880000" } }}EOFCalculate the JWS Signature
You can now calculate the JSON Web Signature (JWS). The JWS consists of the JWS header and claim set hashes in the following format. They are encrypted with the private key.
[JWS Header].[Claim Set]
Follow these steps to calculate the signature:
Concatenate the JWS header and claim set hash strings with a period character (
.) between the hashes:[JWS Header].[Claim Set]Generate an encoded version of the text file using your private key from the .p12 certificate. For more information, see Create or Submit a P12 Certificate.
Base64-encode the signature output.
After calculating the signature, you can construct a complete JWS token by combining the JWS header claims, body claims, and signature.
Example: Token Signature Hash
YjgwNGIxOTMxMzQ2NzhlYjdiMDdhMWZmYjZiYzUzNzliMTk5NzFmNjAzNWRmMThlNzk0N2NhY2U0YTEwNzYyYQEncoding the Signature File Using OpenSSL
Encode the signature file using the openssl tool:
openssl rsautl -encrypt -inkey publickey.key -pubin -in [signature-text-file] > [signature-encoded-file]Base64 Encoding the Signature File Using the Command Line
Encode the signature file using the openssl tool and remove any padding:
base64 -i [signature-encoded-file]Complete the Message with JWTs
Combine the completed JWS token from Steps 3B–3D with the other HTTP headers from Step 3A and your HTTP message body to construct your authenticated REST request message:
Host: Content-Type: application/jsonAuthorization: Bearer <Base64URL-encoded JWS header>.<Base64URL-encoded JWS body claims>.<Base64URL-encoded signature>{ "clientReferenceInformation": { "code": "refnum-123456" }, "paymentInformation": { "card": { "number": "411111111111XXXX", "expirationMonth": "12", "expirationYear": "2031" } }}Set Up Message-Level Encryption
Message-level encryption (MLE) is an additional layer of protection that encrypts the API message body in requests and responses. When MLE is enabled, your request body is wrapped in a JSON Web Encryption (JWE) token before the request is signed and sent.
For request encryption, MLE uses the server public key, which enables only to decrypt the request payload. For response decryption, you use the private key associated with your REST–API Response MLE key.
What to Expect
Review these examples to know what to expect when you enable MLE for your request and response messages:
These examples show an API request before and after MLE encrypts it. After encryption, you can send the request to .
Before Encryption
POST /pts/v2/payments HTTP/1.1Host: Content-Type: application/jsonAuthorization: Bearer <JWS token>{ "clientReferenceInformation": { "code": "refnum-123456" }, "paymentInformation": { "card": { "number": "411111111111XXXX", "expirationMonth": "12", "expirationYear": "2031" } }}After Encryption
POST /pts/v2/payments HTTP/1.1Host: Content-Type: application/jsonAuthorization: Bearer <JWS token>{ "encryptedRequest": "<JWE token>"}These examples show a API response before and after your system uses MLE to decrypt it.
Before Decryption
HTTP/1.1 201 CreatedContent-Type: application/json{ "encryptedResponse": "<JWE token>"}After Decryption
HTTP/1.1 201 CreatedContent-Type: application/json{ "id": "6886040436296843003004", "status": "AUTHORIZED", "clientReferenceInformation": { "code": "refnum-123456" }, "processorInformation": { "approvalCode": "831000", "responseCode": "100" }, "orderInformation": { "amountDetails": { "authorizedAmount": "102.21", "currency": "USD" } }}Before You Begin
Before you can set up message-level encryption (MLE), you must complete these requirements:
- Verify that your system is configured to read the public key and encrypt the API payload.
- Verify that you have a P12 Certificate and extracted its private key.
- Verify that your system can construct JWTs.
Create or Submit a REST—API Response MLE Key
To enable MLE, you must first create a new REST—API response MLE certificate or upload an existing certificate. After creating or uploading the certificate, you can extract the certificate key to begin enabling MLE.
Follow these steps to create or submit an API Response MLE certificate in the :
On the left navigation panel, choose Payment Configuration > Key Management.
Click + Generate key on the Key Management page.
Under REST APIs, choose REST – API Response MLE, and then click Generate key.
Choose one of these options to download your key:
To create a new API response MLE certificate, click Download key .
To upload your own certificate, enter your public PEM-formatted certificate in the text box, and then click Download key . The .pem file downloads to your desktop. If prompted by your system, approve the location to which the file downloads.
If you are creating a certificate, the Set a Password window appears. Create a password for the certificate by entering the password into the New Password and Confirm Password fields, and then click Generate key
The .p12 file downloads to your desktop. If prompted by your system, approve the location to which the key downloads. To create or submit another key, click Generate another key. To view all of your created keys, go to the Key Management page.
Click Cancel.
The Key Management page appears.
Click the Key Type filter and choose REST-API Response MLE.
Click the Expires At filter and choose All Dates.
Click Search.
Find the REST–API Response key that you created in the Search Results table and save its key ID.
The key ID is needed to test and configure your system to use MLE.
Test Your REST–API Response MLE Key
Follow these steps to verify that your REST-API response MLE key is working.
Go to the REST API Reference page in the Developer Center:
On the left navigation panel, choose an API that supports MLE. For testing purposes, you can choose Intelligent Commerce > Intelligent Commerce Product > Enroll a Card.
MLE support is indicated by Request MLE and Response MLE at the top of the screen.
Choose the MLE Configuration tab.
In the Message Level Encryption Credentials section, enter your API response MLE key credentials:
Response encryption: Enter the key ID of your REST—API response MLE key.
You saved this key ID in Step 11 in the Create or Submit a REST—API Response MLE Key section.
Response decryption: Click Browse to submit your own private decryption key from your local system. Only .p12 files are supported.
Click Update Credentials.
From the Send drop-down menu, choose Send Request with Message Level Encryption.
Click Send.
Extract the SJC Certificate
The REST–API Response MLE Key contains an SJC certificate that you must extract in order to enable MLE.
Follow these steps to extract the SJC certificate from your REST–API Response MLE Key:
- Open and use a terminal window to navigate to the REST–API Response MLE Key .p12 file in your system.
- Run this command to open the .p12 file:
openssl pkcs12 -in {certificate_file_name}.p12You are prompted to enter the import password.
- Enter the password you set when you created the REST–API Response MLE Key.
- Locate the
CyberSource_SJC_UScertificate in the opened file. This is the SJC certificate. - Store the SJC certificate in your system to use when you enable MLE.
Set Up MLE
Use the information in this task to configure your system with a custom MLE using JWTs.
Overview of MLE Setup Tasks
Overview Tasks
Import the required programming libraries for your system.
Import these three certificates:
- Signing certificate (P12 Certificate or REST – Certificate)
- MLE request certificate (SJC public certificate)
- MLE response certificate (REST – API Response MLE)
Encrypt the JSON request message using a JSON Web Encryption (JWE) that uses the SJC public certificate.
Create the HTTP body in this format:
{"encryptedRequest": "JWE-with-SJC"}.Create the JSON Web Signature (JWS) payload with these JWT payload fields and your signing certificate's private key:
JWS Header Claim Fields
Header Field Description alg The asymmetric algorithm you use to sign the token header. These algorithms are supported: RS256(default),RS384,RS512,PS256,PS384, andPS512.kid The key ID you use to digitally sign the JWT. It must be registered with the authorizing server. It is the key ID from your P12 certificate. For more information, see Create or Submit a P12 Certificate. typ The token type. Set to JWT.JWS Header Claim Fields
JWS Body Claim Field Description Data Type Field Value Format digestA Base64-encoded hash of the message payload. Do not include the digestfield if the request message is empty, such as during aGETorDELETErequest.String Base64-encoded string: uppercase, lowercase, digits, +,/, and optional=paddingdigestAlgorithmThe algorithm used to hash the message payload. The message payload should be hashed using the SHA-256algorithm. Do not include thedigestAlgorithmfield if thedigestfield is not included.String Lowercase expThe time at which the JWS token expires. Important: field values cannot exceed two minutes after the message issue date, which is the iatfield value. This field is an HTTP-date value as defined in RFC 7231. For example, 01/01/2020 at 00:02:00 is1577836920.String Numeric iatThe date and time at which the message is issued. This field uses a NumericDate value as defined in RFC 7519, which is the number of seconds since 1970-01-01T00:00:00Z (Unix epoch). For example, 01/01/2020 at 00:00:00 is 1577836800.String Numeric issThe issuer identifier for the JWS token. Set to the merchant ID that created the P12 certificate. This value is used to validate the issuer. String Lowercase jtiThe unique token ID. This value is used for replay prevention. Format the value using UUID version 4. For example, 6643fb9a-8093-47c6-95d3-8d69785b5e62.String Lowercase alphanumeric request-hostThe endpoint hostname for the HTTP request, excluding the protocol and path. For example, to send a message to the /pts/v2/payments endpoint, set this field to . String Lowercase alphanumeric with periods request-methodThe HTTP request method. For example, post,get,put,patch, ordelete.String Lowercase request-resource-pathThe endpoint path for the HTTP request, excluding the domain. For example, to send a message to the /pts/v2/payments endpoint, set this field to /pts/v2/payments.String Lowercase alphanumeric v-c-jwt-versionThe Visa JWT scheme version number. Set to 2.String Numeric v-c-merchant-idYour transacting merchant ID (MID). If you are a portfolio or merchant account user, set this to the transacting merchant ID you send requests on behalf of. String Lowercase alphanumeric v-c-response-mle-kidThe message-level encryption response key ID, also known as the REST-API Response MLE key. String Lowercase alphanumeric Sign the JWS with your P12 certificate and send it as
Authorization: Bearer.Receive an encrypted response and decrypt it with the MLE private key. You will receive the response in this format:
{"encryptedResponse": "JWE-with-ResponseMLECertificate"}
The JWE contains a JOSE header containing these four default elements:
"alg": "RSA-OAEP-256", // The algorithm used to encrypt the CEK."enc": "A256GCM", // The algorithm used to encrypt the message."iat": "1702493653", // The current timestamp in milliseconds."kid": "keyId" // The serial number of the v-c-response-mle-kid from the authentication JWS in step 5.Java Example for MLE Enablement
This setup example describes the general requirements to configure your system to support MLE. How you enable MLE in your system can defer from the example code below due to your specific system environment. These example steps use the Java programming language.
- Import your preferred libraries to support MLE. In this step, the configuration uses Java leveraging the open source Nimbus JOSE and Bouncy Castle libraries.
// Nimbus JOSE + JWTimport com.nimbusds.jose.JWEAlgorithm;import com.nimbusds.jose.JWEHeader;import com.nimbusds.jose.JWEObject;import com.nimbusds.jose.JWSAlgorithm;import com.nimbusds.jose.JWSHeader;import com.nimbusds.jose.JWSObject;import com.nimbusds.jose.JOSEObjectType;import com.nimbusds.jose.EncryptionMethod;import com.nimbusds.jose.Payload;import com.nimbusds.jose.crypto.RSADecrypter;import com.nimbusds.jose.crypto.RSAEncrypter;import com.nimbusds.jose.crypto.RSASSASigner;// BouncyCastle (PEM parsing + cert conversion)import org.bouncycastle.cert.X509CertificateHolder;import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;import org.bouncycastle.openssl.PEMKeyPair;import org.bouncycastle.openssl.PEMParser;import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;- Import the signing, MLE, and SJC certificates. The P12 certificate as the signing certificate.
public final class KeyPairMaterial { public final PrivateKey privateKey; public final X509Certificate cert; public KeyPairMaterial(PrivateKey k, X509Certificate c) { this.privateKey = k; this.cert = c; }}public final class CryptoMaterialDual { // Merchant: JWS (REST – Certificate) public final KeyPairMaterial signingCert; // Merchant: Response decryption (API Response MLE) public final KeyPairMaterial responseCert; // Platform encryption cert (SJC) public final X509Certificate sjcCert; public CryptoMaterialDual(KeyPairMaterial signingCert, KeyPairMaterial responseCert, X509Certificate sjcCert) { this.signingCert = signingCert; this.responseCert = responseCert; this.sjcCert = sjcCert; }}- Unpack your imported certificates into a usable format for your system. Create this method for your system to read your .p12 file, if you are using the P12 certificate.
static KeyPairMaterial loadKeyPairFromP12(Path p12Path, char[] password, String keyAlias) throws Exception { KeyStore ks = KeyStore.getInstance("PKCS12"); try (InputStream in = Files.newInputStream(p12Path)) { ks.load( in , password); } KeyStore.PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry) ks.getEntry( keyAlias, new KeyStore.PasswordProtection(password)); return new KeyPairMaterial(entry.getPrivateKey(), (X509Certificate) entry.getCertificate());}Create this method for your system to read the PEM chain and private key.
static KeyPairMaterial loadKeyPairFromPem(Path certificateChainPem, String privateKeyPem) throws Exception { X509Certificate leaf = readPemCerts(certificateChainPem).get(0); PrivateKey key = readPkcs8PrivateKey(privateKeyPem); return new KeyPairMaterial(key, leaf);}Create this method for your system to read the SJC from the .p12 file or PEM chain.
static X509Certificate loadSjcFromP12(Path p12Path, char[] password, String sjcAlias) throws Exception { KeyStore ks = KeyStore.getInstance("PKCS12"); try (InputStream in = Files.newInputStream(p12Path)) { ks.load( in , password); } return (X509Certificate) ks.getCertificate(sjcAlias);}static X509Certificate loadSjcFromPem(Path sjcCertPem) throws Exception { return readPemCerts(sjcCertPem).get(0);}Create this method to include PEM helper functions.
static List < X509Certificate > readPemCerts(Path pemPath) throws Exception { try (Reader r = Files.newBufferedReader(pemPath); org.bouncycastle.openssl.PEMParser p = new org.bouncycastle.openssl.PEMParser(r)) { var xconv = new org.bouncycastle.cert.jcajce.JcaX509CertificateConverter().setProvider("BC"); List < X509Certificate > certs = new ArrayList < > (); Object o; while ((o = p.readObject()) != null) { if (o instanceof org.bouncycastle.cert.X509CertificateHolder h) certs.add(xconv.getCertificate(h)); } return certs; }}static PrivateKey readPkcs8PrivateKey(String pem) throws Exception { try (var parser = new org.bouncycastle.openssl.PEMParser(new StringReader(pem))) { Object o = parser.readObject(); var conv = new org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter().setProvider("BC"); if (o instanceof org.bouncycastle.asn1.pkcs.PrivateKeyInfo pki) return conv.getPrivateKey(pki); if (o instanceof org.bouncycastle.openssl.PEMKeyPair kp) return conv.getPrivateKey(kp.getPrivateKeyInfo()); throw new IllegalArgumentException("Expect PKCS#8 private key PEM"); }}- Create these methods as helpers for encrypting and signing.
static String kidFromCert(X509Certificate cert) { String dn = cert.getSubjectDN().getName().toUpperCase(); int i = dn.indexOf("SERIALNUMBER="); if (i >= 0) { int j = dn.indexOf(",", i); if (j < 0) j = dn.length(); return dn.substring(i + "SERIALNUMBER=".length(), j).trim(); } return cert.getSerialNumber().toString();}static String encryptToJwe(String json, X509Certificate sjcCert) throws Exception { var header = new com.nimbusds.jose.JWEHeader.Builder( com.nimbusds.jose.JWEAlgorithm.RSA_OAEP, com.nimbusds.jose.EncryptionMethod.A256GCM) .contentType("JWT") .keyID(kidFromCert(sjcCert)) .build(); var jwe = new com.nimbusds.jose.JWEObject(header, new com.nimbusds.jose.Payload(json)); jwe.encrypt(new com.nimbusds.jose.crypto.RSAEncrypter((RSAPublicKey) sjcCert.getPublicKey())); return jwe.serialize();}static String sha256Base64(String body) throws Exception { MessageDigest md = MessageDigest.getInstance("SHA-256"); return Base64.getEncoder().encodeToString(md.digest(body.getBytes(StandardCharsets.UTF_8)));}static String signAsJws(String payload, KeyPairMaterial signingCert) throws Exception { var header = new com.nimbusds.jose.JWSHeader.Builder(com.nimbusds.jose.JWSAlgorithm.RS256) .keyID(kidFromCert(signingCert.cert)) .type(com.nimbusds.jose.JOSEObjectType.JWT) // typ=JWT .build(); var jws = new com.nimbusds.jose.JWSObject(header, new com.nimbusds.jose.Payload(payload)); jws.sign(new com.nimbusds.jose.crypto.RSASSASigner(signingCert.privateKey)); return jws.serialize();}static String decryptJwe(String compactJwe, KeyPairMaterial responseCert) throws Exception { var jwe = com.nimbusds.jose.JWEObject.parse(compactJwe); jwe.decrypt(new com.nimbusds.jose.crypto.RSADecrypter((RSAPrivateKey) responseCert.privateKey)); return jwe.getPayload().toString();}- Create a class that uses the methods described in the above steps to encrypt and decrypt your payloads with MLE using JWTs.
// Example mix:// - REST – Certificate from PKCS#12// - API Response MLE from PEM// - SJC from PEMKeyPairMaterial signingCert = loadKeyPairFromP12(Paths.get("merchant.p12"), "password".toCharArray(), "merchant");KeyPairMaterial responseCert = loadKeyPairFromPem(Paths.get("api_response_mle_chain.pem"), Files.readString(Paths.get("api_response_mle_private_key.pem")));X509Certificate sjc = loadSjcFromPem(Paths.get("sjc_certificate.pem"));CryptoMaterialDual mat = new CryptoMaterialDual(signingCert, responseCert, sjc);// 1) Build your request JSONString requestJson = new org.json.JSONObject().put("amount", "10.00").put("currency", "USD").put("reference", "ORDER-12345").toString();// 2) Encrypt request body to JWE using SJC public certString encryptedJwe = encryptToJwe(requestJson, mat.sjcCert);// 3) Build the HTTP body (this is what you’ll hash for the digest)String httpBody = new org.json.JSONObject().put("encryptedRequest", encryptedJwe).toString();// 4) Build JWS payload: include iat, response kid, digestAlgorithm, and digest of httpBodyString digestB64 = sha256Base64(httpBody);String jwsPayload = new org.json.JSONObject().put("iat", java.time.Instant.now().getEpochSecond()).put("v-c-response-mle-kid", kidFromCert(mat.responseCert.cert)) // instruct server to encrypt to your API Response MLE key.put("digestAlgorithm", "SHA-256").put("digest", digestB64).toString();// 5) Sign the JWS with the REST – Certificate private keyString signedJws = signAsJws(jwsPayload, mat.signingCert);// 6) Send the HTTP request// POST /your/api// Content-Type: application/json// Authorization: Bearer <signedJws>/*Body:{ "encryptedRequest": "<encryptedJwe>" }*/// 7) Handle the response (decrypt if needed with API Response MLE private key)String apiResponse = /* http call result as string */;org.json.JSONObject resp = new org.json.JSONObject(apiResponse);String finalPayload = resp.has("encryptedResponse")? decryptJwe(resp.getString("encryptedResponse"), mat.responseCert): apiResponse;Test Your Setup
recommends that you test and verify that your system can securely send and receive REST API messages before transitioning to a production account. Use the test payment examples provided in this section to test your set up. You should also test any additional API requests that you will use in your live environment. For additional API examples, use the developer guide or the REST API Reference:
Troubleshooting Using the REST SDK
You can use the REST Client SDK to review how the SDK constructs, sends and receives JWT messages with MLE. If you are receiving unsuccessful responses from your custom integration, comparing how your system sends and receives messages to the REST SDK can be helpful. For more information about how to install the REST Client SDK into your system, see Set Up REST SDK Integration.
Complete a Test Transaction
After setting up your system to be REST compliant, you can send these test requests to verify that you can send and receive REST API messages.
Authorize a Payment
You send this POST request to the /pts/v2/payments endpoint:
{ "orderInformation": { "billTo": { "country": "US", "lastName": "Kim", "address1": "201 S. Division St.", "postalCode": "48104-2201", "locality": "Ann Arbor", "administrativeArea": "MI", "firstName": "Kyong-Jin", "email": "[email protected]" }, "amountDetails": { "totalAmount": "100.00", "currency": "USD" } }, "paymentInformation": { "card": { "expirationYear": "2031", "number": "4111111111111111", "expirationMonth": "12", "type": "001" } }}Capture an Authorized Payment
You send this POST request to the /pts/v2/payments/{id}/captures endpoint and include the authorization transaction ID as the {id}:
/pts/v2/payments/6461731521426399003473/captures{ "clientReferenceInformation": { "code": "ABC123" }, "orderInformation": { "amountDetails": { "totalAmount": "100.00", "currency": "USD" }}Refund a Captured Payment
You send this POST request to the /pts/v2/payments/{id}/refunds endpoint and include the capture transaction ID as the {id}:
/pts/v2/payments/6772994431376681303954/refunds{ "orderInformation": { "amountDetails": { "totalAmount": "100.00", "currency": "USD" } }}Troubleshooting Errors
If you receive an error message during testing, use this table to determine the cause of the error. For additional error code descriptions, see the Transaction response codes page:
| Symptom | Cause | What to do |
|---|---|---|
HTTP 401 AUTH_FAILED | RSA signature verification failed | Verify merchant ID matches the certificate. Re-check the kid claim equals the certificate serial number. Check timestamp drift (max 15s skew). |
Could not load PKCS12 file | Wrong password, corrupted file, or wrong file format | Try opening the file with openssl pkcs12 -in file.p12 -info to verify it's well-formed. Confirm password is correct. |
BAD_CERTIFICATE in response | Certificate expired or revoked | Test certs expire in 90 days; regenerate. Check the Key Management page for the certificate's status. |
HTTP 401 with no errorInformation | Authorization header malformed | Format must be Bearer <jwt> with one space. JWT must have exactly two dots and three base64url-encoded parts. |
HTTP 400 INVALID_DATA after MLE was added | Body digest doesn't match the JWE bytes | Compute SHA-256 of the encrypted JWE wire string, not the plaintext. |
| Transaction approved in test, denied in production | Test merchant ID was used in a production-credential request | Verify runEnvironment and credentials match — test certs cannot authenticate to the production host. |
Go Live
When you are ready to begin sending live API requests, you must request a production account. A production account sends API requests to the production endpoint, which routes your message to the applicable services, processors, or networks.
Sign Up for a Production Account
Follow these steps to create your production account:
Log in to your test account:
In the , go to Support Cases > MID Configuration Request.
The MID Configuration Request appears.
Click MID Activation.
In the Description field, enter the merchant ID that you want to take live.
Choose a processor configuration, and enter the name of your processor.
If you are unsure of the processor name, contact your merchant service provider or your merchant acquiring bank.
Choose the production environment to apply these change.
Click Service Enablement and list the products and services that you intend to use.
Click Submit.
Establish a Contract
Contact Sales to establish a contract with that enables you to process real transactions and receive support.
Contact Sales to establish a contract with that enables you to process real transactions and receive support.
Contact Sales to establish a contract with that enables you to process real transactions and receive support.
Activate Production Account
Submit a merchant ID (MID) activation request.
It can take up to three business days for the MID to become active.
Create Your Production Credentials
After your production account is created, log in and generate new production credentials. The credentials you created using your test account, such as your API keys, do not automatically transition to the production environment. You must also update your system configuration to now use your new credentials.
Credential Checklist
- P12 certificate
- REST—API Response MLE Key
Next Steps
Your integration is now ready. Choose which solutions to build with next:
Additional Solutions
Billing and Subscriptions
Bill customers on a schedule by sending invoices or creating recurring subscriptions with reusable plans.
View billing guidesPost-Transaction Processing
Track what you process by searching transactions, downloading reports, and keeping stored card data current.
View post-transaction guidesThanks for your feedback!
Last published: September 29, 2026