Collapse Menu
Docs Home
Extensibility Options
Contact Support

Pass Sensitive Data with Secure Requests

Overview

Securely pass data via Store Builder Library.

Store Builder Library (SBL) allows a variety of Store implementation options and checkout flows. Use SBL to perform basic operations like cart manipulation, load coupon codes, or provide known customer details. SBL also allows you to generate advanced checkout sessions that might contain authenticated customer data or custom product pricing. These advanced options are useful when building customized checkout flows and they imply the use of local data encryption.

 

 Tip

An example of how to use secure requests is to provide customers with a name your price or pay what you want option. Design your website to accept price data from your customers and use a secure payload to override the default prices. You can also enable custom pricing with the /sessions endpoint of the FastSpring API.

 

Process Overview

The process of passing data securely using SBL involves three steps:

  1. Creating a session object containing customer and product data
  2. Encrypting the session object locally to protect the data that is to be transmitted
  3. Passing the encrypted payload to FastSpring

FastSpring instantly decrypts the payload upon receipt and applies the decrypted data to the customer's session.

Since JavaScript and other client-side methods are inherently not secure, you must encrypt the sensitive data before passing it to FastSpring. Without a securely encrypted payload, SBL will not accept certain types of session data, such as product price overrides. Thus, even if an adversary were to intercept the data, they could not send FastSpring a modified payload (e.g., with a price of $0.00) without access to the encryption key.

 Important

Although you may make tests using raw/unencrypted data, live orders using the secure methods require encryption. Remember to set up encryption and pass only encrypted payloads before going live with these methods. See Passing an Encrypted Payload to the Store Builder Library, later on in this article, for more information.

 

Pass Customer Information to be Applied to the Order in the Secure Payload

 Tip

Our articles Customer Information, Accounts and single sign-on and Using Store Builder Library to Provide Customer Details can help you familiarize yourself with the concept of Customer Accounts and various options around applying customer information.

When you have authenticated your customer and your backend is aware of customer details, you can pass the details to the checkout process.

Payload Example
{
    contact: {                                              // customer details - optional unless you pass accountCustomKey (below); if you pass customer contact info, the Storefront does not prompt the customer to provide it during checkout
        firstName: <String>                                 // customer first name - required when passing contact info
        lastName: <String>                                  // customer last name - required when passing contact info
        email: <String>                                     // customer email address - required when passing contact info
        company: <String>                                   // customer company (optional unless required by Storefront settings)
        addressLine1: <String>                              // customer address line 1 (optional unless order includes physical product or 'Force phsyical address collection' is enabled in Storefront settings) 
        addressLine2: <String>                              // customer address line 2 (optional)
        city: <String>                                      // customer city (optional unless order includes physical product or 'Force physical address collection' is enabled in Storefront settings)
        region: <String>                                    // customer region (e.g. state for US customers, recommend two-character ISO region code)(optional unless order includes physical product or 'Force physical address collection' is enabled in Storefront settings)
        postalCode: <String>                                // customer postal code (required for credit card transactions)
        phoneNumber: <String>                               // customer phone number (optional unless required by Store settings)
        country: <String>                                   // customer country - two-character ISO country code (optional unless order includes physical product or 'Force physical address collection' enabled in Storefront settings)
    },
    accountCustomKey: <String>,                             // optional account ID from your system; if you pass this then the contact object above is required
    account: <String>,                                      // FastSpring-generated customer account ID (optional)
    taxId: <String>,                                        // optional GST ID or VAT ID for the customer; will be validated in conjunction with the country (detected via geo IP location or set as part of the contact object); if the ID is valid, it will be applied to the session and no GST or VAT will be applied to the order
}

 Tip

When passing contact information via the secure payload, your payload must include emailfirstName, and lastName at a minimum. If you only have the customer's email address, and you do not need to pass sensitive data such as product price overrides, consider using the session object instead of a secure payload.

Important: When using a secure payload, FastSpring validates customer details provided inside the contact object upon submission. If the data does not pass validation, the Store treats the data in the same way as a fastspring.builder.recognize() call. In this case, the data is pre-filled, but fields are visible and editable during the checkout. This approach allows customers to correct their mistakes. However, in the case of address fields, if your Storefront is not configured to display those fields, the customer does not see the fields and so cannot correct the problem. Therefore, if address validation is important to your business, we encourage you to do one of the following:

  • validate the address supplied by the customer before passing it in via a secure payload
  • ensure that your Storefront has the Force physical address collection for all orders checkbox selected (on the Checkout page of a Popup Storefront's Settings, or the General Settings page for a Web Storefront's Settings) so that the fields are displayed when necessary

 Tip

As an alternative to encrypting the data and passing it on the frontend, you can use the /accounts endpoint of the FastSpring API to create or modify accounts from the backend. The account ID obtained in the /accounts response can then be used with the /sessions endpoint to create a complete session. However, a mixed approach is not possible. If you pass both an account ID and the contact object in your SBL payload, the Store ignores the contact object in favor of the established account details.

 

Pass Gift Recipient Information to be Applied to the Order in the Secure Payload

This method automatically selects the Gift Purchase checkbox in the checkout form and allows you to pass authenticated recipient information to the library, so that Recipient Information fields of the order form are pre-filled and not displayed to the customer (pending validation).

Payload Example
{
	"recipient": {                                          // gift recipient details
		"email": "ne1@all.com",            // recipient email address (must differ from the contact email address, if provided)
		"firstName": "firstName",                       // recipient first name
		"lastName": "lastName",                         // recipient last name
		"address": {                                    // recipient address information
			"addressLine1": "Address Line 1",       // recipient address line 1 (optional unless order includes physical product or 'Force physical address collection' is enabled in Storefront settings)
			"city": "City",                         // recipient city (optional unless order includes physical product or 'Force physical address collection' is enabled in Storefront settings)
			"region": "California",                 // recipient region (e.g. state for US customers, recommend two-character ISO region code)(optional unless order includes physical product or 'Force physical address collection' is enabled in Storefront settings)
			"postalCode": "93101",                  // recipient postal code (optional unless purchaser contact is in the United States, in which case this must be a valid U.S. ZIP code)
			"country": "US"                         // recipient country - two-character ISO country code (must match purchaser contact country)
		},
		"memo": "Happy Birthday!"                       // optional personal message from purchaser to recipient (maximum 400 characters)
	}
}

 

Pass Product Information in the Secure Payload

This approach is useful when you want to change a product's price for a specific customer. For example, this might be the case with special pricing or price A/B testing. To do this, you can use "tags" and "attributes" to pass experiment/variation IDs. Tags provide a way to assign arbitrary key/value pairs to the order and "capture" them later in webhooks and API responses.

 Tip

When passing the pricing object for a subscription product in your payload, be sure to include trialintervalintervalLength, and intervalCount. Passing the pricing object overrides the product's existing pricing data. Therefore, if you pass the pricing object without subscription details, the product is processed as a stand-alone, single-payment product.
Payload Example
{
    items: [
        {
            product: ,                                                 // id of the product you are adding to the cart
            quantity: ,                                                // quantity of the current product to be added to the cart
            pricing: {                                                 // keep in mind - when you pass the "pricing" object for an item you need to define all possible pricing options, including, for example, subscription-related fields where applicable. 
                trial: ,                                               // number of free trial days for a subscription (required for subscription only)
                interval: "adhoc", "day", "week", "month", or "year",  // interval unit for scheduled billings; "adhoc" = managed / ad hoc subscription (required for subscription only)
                intervalLength: ,                                      // number of interval units per billing (e.g. if interval = "MONTH", and intervalLength = 1, billings will occur once per month)(required for subscription only)
                intervalCount: ,                                       // total number of billings; pass null for unlimited / until cancellation subscription (required for subscription only)
                quantityBehavior: "allow", "lock", or "hide",          // "allow" = quantity can be edited, "lock" = quantity is displayed in the checkout but not editable, "hide" = quantity is hidden and not editable (optional, default is allow)
                quantityDefault: ,                                     // default quantity for the product (only needed if you do NOT pass quantity above)
                price: {                                               // multiple currencies can be passed
                   "currency_code":  (e.g. "USD": 5.0),
                   ...
                },
                discountType: "percent", or "amount",                           // optional product-level discount type
                quantityDiscounts: {                                            // optional volume-based discount settings; specify quantity required to receive discount per unit followed by discount percentage or amount 
                    : {                                                // ex. if discountType = "PERCENT", 10: 25.00 results in 11+ units each receiving a 25% discount; for a simple discount use 0: 10.00 to give a 10% discount off everything 
                       "currency_code": "Number" (e.g. "USD": 5.0),             // ex. if discountType = "AMOUNT",  5: {"USD": 5.0} results in 6+ units each receiving a discount of $5 USD; multiple currencies supported
                       ...
                    },
                    ...
                },
                discountReason: {                                               // reason for the discount, which can optionally be displayed to customers; specified language must be enabled in Store settings
                    "language_code":  (e.g. "DE": "German text"),
                    ...
                },
                discountDuration: ,                                    // number of subscription billings to which the discount will apply (subscription only); e.g. 1 = first billing only, 2 = first and second billings, etc.
                dateLimitsEnabled: ,                                   // controls whether or not the discount is valid only during a certain date range 
                startDate: ,                                           // earliest date on which the discount applies
                endDate: ,                                             // discount end / expiration date
                startTime: ,                                           // time on the earliest date when discount becomes available; specify time in UTC
                endTime: ,                                             // time on the end / expiration date when discount is no longer available; specify time in UTC
                reminder_enabled: ,                                    // controls whether or not payment reminder email messages are enabled for the product (subscription only)
                reminder_value:  "adhoc", "day", "week", "month", or "year",    // interval unit for payment reminder email messages (subscription only)
                reminder_count: ,                                      // number of interval units prior to the next billing date when payment reminder email messages will be sent (subscription only)
                payment_overdue: ,                                     // controls whether or not payment overdue notification email messages are enabled for the product (subscription only)
                overdue_interval_value: "adhoc", "day", "week", "month", or "year",  // interval unit for payment overdue notification email messages (subscription only)
                overdue_interval_count: ,                              // total number of payment overdue notification messages to send (subscription only)
                overdue_interval_amount: ,                             // number of overdue_interval units between each payment overdue notification message (subscription only)
                cancellation_interval_count: ,                         // number of cancellation_interval units prior to subscription cancellation (subscription only) 
                cancellation_interval_value:  "adhoc", "day", "week", "month", or "year",  // interval unit for subscription cancellation (subscription only)
            },
            display: {                                                          // customer-visible product display name or title
                "language_code":  (e.g. "DE": "German text"),           // multiple languages supported, language must be enabled in Store settings
                ...
            },
            description: {                                                      // customer-visible product description
                "values": {
                    "full": {
                        "values": {
                            "language_key": "value" (e.g "en": "English description"),  // multiple languages supported, language must be enabled in Store settings
                            ...
                        }
                    }
                }
            },
            image: ,                                                   // product icon image file URL
            selected: ,                                                // controls whether or not product is currently selected / in the cart
            removable: ,                                               // controls whether or not product can be removed from the cart by the customer
            attributes: {
                key: ,                                                 // optional product attributes (maximum of approximately 4,000 characters)
                ...
            },
            sku:                                                       // optional product SKU ID (e.g. to match your internal SKU or product ID)
        },
        ...
    ],
    tags: {                                                            // optional order tags (maximum approximately 4,000 characters)

        key: ,
        ...
    }
}

You can pass customer, recipient, and product information together in a single payload.

 

Pass Other Order Details in the Secure Payload

In addition to customer and product information, you can also optionally pass the following order details via the secure payload:

  • a coupon code
  • the country to be used for the order (which controls the currency for the order)
  • the order language
Payload Example
{
    coupon: ,                                       // optional coupon code (not ID) to be applied to the order
    country: ,                                      // customer country - pass two-character ISO country code (e.g., "DE" for Germany); controls transaction currency
    language: ,                                     // customer language - pass two-character ISO language code (e.g., "DE" for German); controls language used in checkout
}

 

Authenticate Customers to Redirect to Account Management

If you have the customer's FastSpring account ID on record, you can generate a temporary, pre-authenticated account management URL. This process lets you create single sign-on functionality so that the customer can log on to FastSpring's account management site without an extra validation step.

Encrypt the following JSON on the backend:

{

"account": "ID obtained from FS",

"timestamp": 1427419618678 /* epoch time in milliseconds */

}

Then, call the fastspring.builder.authenticate(securedData, secureKey) method, passing the secure data and the secure key as described below. The customer will be redirected to the account management page and logged on automatically.

 

Flow

  1. Generate a Private/Public key pair.
  2. Build a JSON object (as a string) that contains the required data/actions on your backend.
  3. Generate a "Secure Key" and encrypt the JSON object (as a string) using the Private key.
  4. Pass the resulting strings (the encrypted payload and the secure key) to your frontend and assign them to JavaScript variables.
  5. Make sure your webpage provides the "data-access-key" when initializing the Library; otherwise, the secure payload won't be sent. 

 

Create Encrypted Payloads

Set Up Encryption

  1. In the FastSpring App, select the Integrations menu and the Store Builder Library tab. 
  2. Copy the Access Key. This is the access key with which you will initialize the library via "data-access-key".
  3. 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.
  4. Click Save.

 

Generate the "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 the FastSpring App
**/
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.

 

Pass an Encrypted Payload to the Store Builder Library

Option 1: Build the session object before the API is initialized

 Tip

This method implies understanding the 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 apply the data in the first Library call to the 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.8.3/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>

 

Test 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:

To test your payload:

  • Pass JSON as an object to your frontend.
  • Send the raw JSON object in a secure payload call leaving the "secureKey" empty. 
<script>
    fastspring.builder.secure(nonEncryptedJSON, '');
</script>

 

Try FastSpring

Get a free account and see why FastSpring is the ecommerce partner of choice for software providers around the world. Try our full-service ecommerce solution today to unlock revenue growth for your online company.