Pass sensitive customer or pricing data securely using an encrypted payload
Secure payloads
Use secure payloads to prefill customer details, pricing, or other sensitive data in your Store Builder checkout. This method encrypts the payload on your server using a signed key pair and passes it to FastSpring using fscSession
or fastspring.builder.secure()
. FastSpring decrypts the payload at runtime and applies it to the session.
Note: Secure payloads are typically used to lock fields like name, email, price, or tax ID. This method is ideal for passing verified customer data that should not be editable during checkout.
Why use secure payloads?
Secure sensitive values that should not be visible or editable at checkout:
- Lock contact fields: Prevent buyers from modifying name, email, or address
- Apply validated IDs: Include tax IDs or account IDs without exposing them
- Restrict payment methods: Pre-select and enforce a specific payment option
What this guide covers
This guide explains how to:
- Set up encryption for secure payloads
- Create and encrypt a secure session payload
- Pass the payload using
fscSession
orfastspring.builder.secure()
- Work with secure test payloads in development
- Understand accepted fields and payload structure
Set up encryption
Before you can pass a secure payload to your store checkout, you need to complete a one-time encryption setup in your FastSpring account.
-
In the FastSpring App, go to Developer Tools > Store Builder Library.
-
Copy your Access Key. You will use this in the
data-access-key
attribute when initializing Store Builder. -
Generate a 2048 bit RSA key pair.
- Create a private key and save it as
privatekey.pem
. Keep this file secure and do not share it. You will use it on your server to encrypt the payload and generate thesecureKey
. - Create a public key and save it as
publiccert.pem
. Share this with FastSpring only. FastSpring will use it to decrypt your payload at checkout.
If you haven’t done this before, check out resources on generating RSA key pairs in your preferred language or tooling (like OpenSSL or Node crypto). Make sure your key length is 2048 bits.
- Create a private key and save it as
-
Upload your
publiccert.pem
file using the File Upload section. -
Click Save.
You will use the private key to encrypt session data on your server and generate the secure payload. FastSpring will use the public key to decrypt it at checkout.
Create and encrypt your payload
After setting up encryption, you can create a secure payload on your server and pass it into Store Builder. A secure payload is a JSON object containing session data that is encrypted using your private key. FastSpring uses your uploaded public key to decrypt the payload at checkout.
Only supported fields should be included in your payload. Fields like
reset
andcheckout
are not valid in secure session objects.
The exact implementation depends on your backend language, but the core process is the same across platforms.
Example payload before encryption
Here’s a basic example of what your unencrypted session data might look like before you encrypt it. This is the structure you will stringify and encrypt to create your securePayload
.
{
"contact": {
"firstName": "Jane",
"lastName": "Smith",
"email": "[email protected]",
"country": "DE"
},
"items": [
{
"product": "example-product",
"quantity": 1,
"pricing": {
"price": {
"USD": 19.00
}
}
}
]
}
Tip: You can include address fields inside the
contact
object or as a top-leveladdress
object. If your store requires physical address collection, make sure it is enabled in your store settings.
How to encrypt the payload
You will generate the securePayload
and secureKey
in your backend using your private key. The logic is the same regardless of language:
- Encrypt the payload with a randomly generated AES key
- Encrypt that AES key using your private RSA key (
privatekey.pem
) - Pass both encrypted values to Store Builder at checkout
Java
Follow these steps to create your secure payload in Java:
- Generate a 16-byte AES key
You will use this key to encrypt your session data before passing it to FastSpring.
final KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
final SecretKey secretKey = keyGenerator.generateKey();
final byte[] aesKey = secretKey.getEncoded();
- Encrypt the payload using AES-128-ECB with PKCS5 padding
This creates yoursecurePayload
string, which contains the encrypted session data.
final SecretKeySpec secretKeySpec = new SecretKeySpec(aesKey, "AES");
final Cipher encryptCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
encryptCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
final byte[] cleartext = unencryptedString.getBytes("UTF-8");
final byte[] ciphertext = encryptCipher.doFinal(cleartext);
final String securePayload = Base64.getEncoder().encodeToString(ciphertext);
- Encrypt the AES key using your private RSA key
Use theprivatekey.pem
file you created during the encryption setup step. This creates thesecureKey
.
final org.bouncycastle.openssl.PEMReader pemReader =
new org.bouncycastle.openssl.PEMReader(new StringReader(privateKeyPEM));
final KeyPair keyPair = (KeyPair) pemReader.readObject();
pemReader.close();
final PrivateKey privateKey = keyPair.getPrivate();
final Cipher clientPrivateCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
clientPrivateCipher.init(Cipher.ENCRYPT_MODE, privateKey);
final byte[] aesKeyEncrypted = clientPrivateCipher.doFinal(aesKey);
final String secureKey = Base64.getEncoder().encodeToString(aesKeyEncrypted);
FastSpring will use your uploaded publiccert.pem
to decrypt the secureKey
and unlock the securePayload
at checkout.
PHP
Follow these steps to create your secure payload in PHP:
- Generate a 16-byte AES key
You will use this key to encrypt the session data before passing it to FastSpring.
$aesKey = openssl_random_pseudo_bytes(16);
- Encrypt the payload using AES-128-ECB
This creates thesecurePayload
string, which contains the encrypted session data.
$cipherText = openssl_encrypt($unencryptedString, "AES-128-ECB", $aesKey, OPENSSL_RAW_DATA);
$securePayload = base64_encode($cipherText);
- Encrypt the AES key using your private RSA key
Use theprivatekey.pem
file you created during the encryption setup step. This creates thesecureKey
.
$privateKeyFileName = realpath("privatekey.pem");
$privateKey = openssl_pkey_get_private("file://$privateKeyFileName");
openssl_private_encrypt($aesKey, $aesKeyEncrypted, $privateKey);
$secureKey = base64_encode($aesKeyEncrypted);
FastSpring will use your uploaded publiccert.pem
to decrypt the secureKey
and unlock the securePayload
at checkout.
Pass the secure payload
Once you've created your securePayload
and secureKey
, you can pass them to Store Builder using either fscSession
or fastspring.builder.secure()
.
Use fscSession
before Store Builder loads
fscSession
before Store Builder loadsApply the encrypted data when the page loads by defining fscSession
before Store Builder initializes.
<script>
var fscSession = {
secure: {
payload: securePayload, // encrypted payload string generated on your server
key: secureKey // encrypted AES key string passed from your backend
}
};
</script>
<!-- Define the session object before loading the Store Builder script -->
<script
id="fsc-api"
src="https://sbl.onfastspring.com/sbl/1.0.3/fastspring-builder.min.js"
type="text/javascript"
data-storefront="your-storefront-url"
data-access-key="your-access-key">
</script>
Use fastspring.builder.secure()
after Store Builder loads
fastspring.builder.secure()
after Store Builder loadsYou can also pass the secure payload dynamically using fastspring.builder.secure()
after the script has initialized.
<script>
fastspring.builder.secure(securedData, secureKey);
</script>
This method is helpful when your payload is generated on the fly or fetched from your backend after the page loads.
Test with unencrypted payloads (development only)
If you are placing a test order, you can pass private information unencrypted for testing purposes only. This allows you to troubleshoot and adapt your payload and encryption logic before going live.
<script>
fastspring.builder.secure(nonEncryptedJSON, "");
</script>
Accepted fields
The following fields are supported in secure payloads. Field requirements may vary depending on your store settings and the data you pass. Address fields can be included either inside the contact
object or as a separate top-level address
object.
If your store requires physical address collection, make sure that option is enabled in your store settings.
Contact information
These fields allow you to include customer and account information in your secure payload. You can structure address fields inside the contact
object or as a top-level address
object.
Field | Type | Description |
---|---|---|
contact |
object | Customer contact information. Required if you pass accountCustomKey . Fields inside contact will not be editable at checkout. |
address |
object | Optional top-level object for address fields. Can be used instead of nesting address inside contact . |
contact.firstName |
string | Customer’s first name. Required when passing contact . |
contact.lastName |
string | Customer’s last name. Required when passing contact . |
contact.email |
string | Customer’s email address. Required when passing contact . This must be a valid email format. Invalid emails will trigger validation errors. |
contact.company |
string | Customer’s company name. Required if your store settings require it. |
contact.addressLine1 or address.addressLine1 |
string | Address line 1. Required for physical products or if address collection is enabled. |
contact.addressLine2 or address.addressLine2 |
string | Address line 2 (optional). |
contact.city or address.city |
string | City. Required for physical products or address collection. |
contact.region or address.region |
string | Region or state (2-letter ISO). Required for US-based physical orders. |
contact.postalCode or address.postalCode |
string | Postal code. Required for credit card transactions. |
contact.phoneNumber or address.phoneNumber |
string | Phone number. Required if your store settings require it. |
contact.country or address.country |
string | Two-letter ISO country code. Required for physical products or tax purposes. |
accountCustomKey |
string | Account ID from your system. Requires contact to be passed. |
account |
string | FastSpring-generated account ID (optional). |
taxId |
string | VAT ID or GST ID. FastSpring will validate this against the customer’s IP or contact country. |
Example: Contact and account fields
Here’s a full example showing how to pass contact information, tax ID, and account identifiers in a secure payload. Fields shown here are optional unless otherwise noted in the Contact information table.
{
"contact": {
"firstName": "Jane",
"lastName": "Doe",
"email": "[email protected]",
"company": "Example Co.",
"addressLine1": "456 Example Ave",
"addressLine2": "Suite 789",
"city": "San Francisco",
"region": "CA",
"postalCode": "94107",
"phoneNumber": "555-123-4567",
"country": "US"
},
"accountCustomKey": "external-id-456",
"account": "fs-account-id-abc123",
"taxId": "GB123456789"
}
Payment method settings
You can pre-select a preferred payment method and optionally hide all other options from the buyer during checkout.
Field | Type | Description |
---|---|---|
paymentMethod |
string | Pre-selects a specific payment method for the customer. |
hideOtherPaymentMethods |
boolean | If true, only the selected payment method is shown at checkout. |
Example: Pre-select a payment method
Here’s a short example that pre-selects PayPal and hides all other payment methods during checkout.
{
"contact": {
"firstName": "Jane",
"lastName": "Doe",
"email": "[email protected]",
"country": "US"
},
"items": [
{
"product": "example-product",
"quantity": 1,
"pricing": {
"price": {
"USD": 19.00
}
}
}
]
"paymentMethod": "paypal",
"hideOtherPaymentMethods": true
}
Be sure the selected payment method is enabled in your store settings. Otherwise, it will be ignored at checkout.
Product information
These fields define the products added to the cart using the items
array. You can specify quantities, pricing, trial periods, display settings, and more.
Field | Type | Description |
---|---|---|
items |
array | A list of product objects to add to the cart. Each item represents one product and its configuration. |
items.product |
string | The product path or ID from your FastSpring store. |
items.quantity |
integer | The number of units added to the cart. Ignored if quantityBehavior is set to lock or hide . |
items.pricing.price |
object | Product price by currency. Example: { "USD": 19.00, "EUR": 17.00 } . |
items.pricing.trial |
integer | Number of free trial days for subscriptions. |
items.pricing.interval |
string | Billing frequency for subscriptions. Accepts adhoc , day , week , month , or year . |
items.pricing.intervalLength |
integer | Number of units per billing interval. For example, 1 = every month, 3 = every 3 months. |
items.pricing.intervalCount |
integer | Total number of billings before the subscription ends. Use null for ongoing subscriptions. |
items.pricing.quantityBehavior |
string | Controls how quantity is displayed at checkout. Options: allow , lock , or hide . |
items.pricing.quantityDefault |
integer | Default quantity shown at checkout. Required if you don’t pass quantity above or if quantityBehavior is lock or hide . |
items.display |
object | Localized display name for the product. Example: { "en": "Product Name" } . |
items.description |
object | Localized product description. Requires language keys enabled in your store settings. |
items.image |
string | URL for the product image shown during checkout. |
items.selected |
boolean | Determines whether the product appears pre-selected in the cart. |
items.removable |
boolean | Allows or prevents the customer from removing the product at checkout. |
items.sku |
string | Internal SKU or product identifier used by your system. |
items.attributes |
object | Optional product attributes. Useful for tracking, integrations, or custom logic. |
tags |
object | Optional key-value pairs added at the order level. Accessible in webhooks and server integrations. |
Example: Full product payload structure
Here’s a complete example of a secure payload with detailed product configuration, including subscription settings, pricing, display content, and optional metadata.
{
"items": [
{
"product": "product-path",
"quantity": 1,
"pricing": {
"trial": 30,
"interval": "month",
"intervalLength": 1,
"intervalCount": null,
"quantityBehavior": "allow",
"quantityDefault": 1,
"price": {
"USD": 5.0,
"EUR": 4.0
},
"discountType": "percent",
"quantityDiscounts": {
"1": {
"USD": 5.0,
"EUR": 4.0
}
},
"discountReason": {
"DE": "German text"
},
"discountDuration": 1,
"dateLimitsEnabled": true,
"startDate": "2025-01-01",
"endDate": "2025-12-31",
"startTime": "00:00",
"endTime": "23:59",
"reminder_enabled": true,
"reminder_value": "day",
"reminder_count": 1,
"payment_overdue": true,
"overdue_interval_value": "day",
"overdue_interval_count": 1,
"overdue_interval_amount": 3,
"cancellation_interval_count": 1,
"cancellation_interval_value": "day"
},
"display": {
"DE": "German text"
},
"description": {
"values": {
"full": {
"values": {
"en": "English description"
}
}
}
},
"image": "https://example.com/image.png",
"selected": true,
"removable": true,
"attributes": {
"key": "value"
},
"sku": "internal-product-sku-1234"
}
],
"tags": {
"key": "value"
}
}
Tip: This example includes supported fields for subscriptions, pricing rules, and product display. You can simplify the structure based on your needs. Not all fields are required, so only include what applies to your product.
FAQs
Do I need to encrypt every session?
No. Use encryption only when you are passing sensitive information that should not be visible or editable by the customer. For example, when locking in verified contact information, fixed pricing, or a validated tax ID. If you do not need to restrict any fields, you can use a standard session object without encryption.
Can I reuse secure payloads across sessions?
No. Secure payloads are designed for one-time use per session. You should generate a new payload for each checkout to ensure the data remains accurate, valid, and secure.
Can I include fields that aren’t listed in the accepted fields table?
No. Fields not listed in the accepted fields table will be ignored. FastSpring will only process supported fields, so it's important to match the structure and field names exactly. If you're unsure about a specific field, test it in development mode or contact support for guidance.
What happens if I pass address fields in both contact
and address
?
FastSpring will prioritize fields in the contact
object. Use either contact
or address
, but not both. Duplicating fields between them may cause unexpected behavior.