Passing Sensitive Data with Secure Requests
This article applies to Contextual Commerce. (Looking for Classic Commerce documentation?)
Along with basic operations like cart manipulation and providing coupons or "known" customer details, Store Builder Library allows you to generate advanced checkout sessions which might contain "authenticated" customer data, custom product pricing or product definitions. This is useful when building customized checkout flows and implies the use of data encryption.
Tip
What can be passed as part of secure payload
The payload might contain the following:
- Customer information / user account ID
- Gift recipient information
- Custom product pricing and subscription definitions
- Account management authentication
Passing Customer Information to Be Applied to the Order in the Secure Payload
We recommend reading Customer Information, Accounts and single sign-on and Using Store Builder Library to Provide Customer Details to familiarize yourself with the concept of Customer Accounts and various options around applying customer information.
This is required when your customer is authenticated with you and your backend is aware of customer details and the details can be passed to the checkout process.
Passing Product Information in the Secure Payload
Customer, recipient, and product information can all be passed in a single payload.
Passing Other Order Details in the Secure Payload
In addition to customer and product information, you can also optionally include a coupon, specify the country to be used for the order (which controls the currency for the order) and specify the order language via the secure payload.
{ coupon: <String>, // optional coupon code (not ID) to be applied to the order country: <String>, // customer country - pass two-character ISO country code (e.g., "DE" for Germany); controls transaction currency language: <String>, // customer language - pass two-character ISO language code (e.g., "DE" for German); controls language used in checkout }
Authenticating Customers to Redirect to Account Management
If your backend is aware of the customer's FastSpring account ID you can generate a pre-authenticated URL and redirect customer to the account management area without the additional validation.
Encrypt the following JSON on the backend:
{ "account": "ID obtained from FS", "timestamp": 1427419618678 /* epoch time in milliseconds */ }
And call fastspring.builder.authenticate(securedData, secureKey) method passing secure data and secure key as described below. The customer will be redirected to the account management page.
Flow
- Generate Private/Public key pair
- Build a JSON object (as string) that contains the required data/actions on your backend.
- Generate "Secure Key", encrypt JSON object (as string) using the Private key.
- Pass resulting strings (encrypted payload and secure key) to your frontend and assign them to Javascript variables.
- Make sure you provided "data-access-key" when initializing the Library, otherwise the secure payload won't be sent.
Creating encrypted payloads
Setting Up Encryption
- In the Dashboard, select the Integrations menu and the Store Builder Library tab.
- Copy the Access Key. This is the access key with which you will initialize the API.
- Create a Private/Public certificate pair (see instructions below). Under File Upload, click Choose File and browse to and select the file containing your public certificate.
- Click SAVE.
Generating "securePayload" and "secureKey"
One-Time Only - Create Private and Public Keys
- Create a 2048-bit RSA private key. Do not share this key. You will use the private key PEM file privatekey.pem to create your "secureKey".
openssl genrsa -out privatekey.pem 2048
- Create a 2048-bit RSA public key. Only share this key with FastSpring. FastSpring will use your public key PEM file publiccert.pem to decrypt your payload.
openssl req -new -key privatekey.pem -x509 -days 3650 -out publiccert.pem
Create Your "Secure Key" and "Secure Payload" for Each Request
When preparing a secure request encrypt your generated payload.
Using Node.js
/** * Encrypts a JSON payload given the private key * corresponding to the public key stored in Dashboard **/ const crypto = require('crypto'); module.exports = function encrypt(payload, privateKey) { const aesKey = crypto.randomBytes(16); const iv = new Buffer(""); const cipher = crypto.createCipheriv('aes-128-ecb', aesKey, iv); var encryptedPayload = cipher.update(new Buffer(payload, 'utf8'), 'utf8', 'base64'); const securePayload = encryptedPayload + cipher.final('base64'); const secureKey = crypto.privateEncrypt(privateKey, aesKey).toString('base64'); return { securePayload: securePayload, secureKey: secureKey }; };
Using Java
- Create a new random 16-byte aesKey for each payload.
final KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(128); final SecretKey secretKey = keyGenerator.generateKey() ; final byte[] aesKey = secretKey.getEncoded() ;
- Use the random 16-byte aesKey to create the securePayload string from your unencryptedString (typically Base64 string, but could be raw byte[]).
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);
- Use your privateKeyPEM string to encrypt your aesKey to create your secureKey string.
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 uses your secureKey string and publiccert.pem to decrypt your securePayload string.
Using PHP
You can also download the working example from here: encryption.php
- Create a new random 16 byte aesKey for each payload.
$aesKey = openssl_random_pseudo_bytes(16) ; # or urandom() or any other random byte generator
- Use the random 16 byte aesKey to create the securePayload string from your unencryptedString (typically Base64 string, but could be raw byte[]).
$cipherText = openssl_encrypt($unencryptedString, "AES-128-ECB", $aesKey, OPENSSL_RAW_DATA) ; $securePayload = base64_encode($cipherText) ;
- Use your privatekey.pem file to encrypt your aesKey to create your secureKey string.
$privateKeyFileName = realpath("privatekey.pem") ; $privateKey = openssl_pkey_get_private("file://$privateKeyFileName") ; openssl_private_encrypt($aesKey, $aesKeyEncrypted, $privateKey) ; $secureKey = base64_encode($aesKeyEncrypted) ;
FastSpring uses your secureKey string and publiccert.pem to decrypt your securePayload string.
Passing encrypted payload to the Store Builder Library
Option 1: Build the sessionObject before the API is initialized
This method implies understanding a concept of the Session Object. Refer to Accessing the Library from Javascript for more information.
This method works best if a secure payload is available to the page at the moment the page loads. This way you will also apply the data in the first Library call to server.
<script> var fscSession = { 'secure': { 'payload': securedData, // string of encrypted data passed from the server 'key': secureKey // secure key passed from the server } } </script> <!-- placing the session object before the Library is initialized ensures that the data will be sent with the very first request --> <script id="fsc-api" src="https://d1f8f9xcsvx3ha.cloudfront.net/sbl/0.7.6/fastspring-builder.min.js" type="text/javascript" ...
Option 2: Use the fastspring.builder.secure() method after the API is initialized
<script> fastspring.builder.secure(securedData, secureKey); </script>
Testing secure payloads
To simplify the testing process, "Test" storefronts accept unencrypted payloads. Each storefront can be used in both Live and Test modes simultaneously. For example:
- vendor.onfastspring.com is the Live URL of the storefront
- vendor.test.onfastspring.com is the Test URL of the same storefront
To test your payload:
- Pass JSON as object to your frontend
Send JSON object in a secure payload call leaving "key" empty.
<script> fastspring.builder.secure(nonEncryptedJSON, ''); </script>