Pass encrypted payloads to FastSpring.
You can use the Store Builder Library (SBL) to pass sensitive information in an encrypted format. This is necessary to hide information that customers should not have access to. To secure the information, follow the steps below:
FastSpring will decrypt the payload and apply the information to the associated order session.
Set Up Encryption
This step is only necessary for initial setup. If you have set up encryption before, you can skip this step.
- Navigate to Developer Tools > Store Builder Library.
- Copy the Access Key. You will use this key and data-access-key to initialize the SBL.
- Create a Private/Public certificate pair.
- Private: 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.
- Public: 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.
- Under File Upload, click Choose File. Select the file containing your public certificate.
- Click Save.
Create a Session Object
Create a session object containing all of the information which you would like to encrypt. The required fields will vary depending on the information collected.
<script>
var fscSession = {
'reset': true,
'products' : [
{
'path':'example-product',
'quantity': 1
}
],
'coupon': 'YOUR10OFF'
}
</script>
<script
id="fsc-api"
src="https://d1f8f9xcsvx3ha.cloudfront.net/sbl/0.8.7/fastspring-builder.min.js"" type="text/javascript"
Locally Encrypt the Session Object
After you create the session object, locally encrypt the data. This varies depending on the programming language you are using. FastSpring will use your secureKey string and publiccert.pem to decrypt your securePayload string.
Java
- Create a 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 AES key to create the securePayload string from your unencryptedString. This is typically a Base64 string, but it can be a 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 privateKey PEM 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.
PHP
- Create a 16 byte AES key for each payload.
$aesKey = openssl_random_pseudo_bytes(16) ; # or urandom() or any other random byte generator
- Use the random 16-byte AES key to create the securePayload string from your unencryptedString. This is typically a Base64 string, but it can be a raw byte.
$cipherText = openssl_encrypt($unencryptedString, "AES-128-ECB", $aesKey, OPENSSL_RAW_DATA) ;
$securePayload = base64_encode($cipherText) ;
- Encrypt your AES key with the privatekey.pem file. This creates 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 will use your secureKey string and publiccert.pem to decrypt your securePayload string.
Pass a Preferred Payment Method
You can set a preferred payment method by passing two properties into the fastspring.builder.secure()
function:
Field | Data Type | Description |
---|---|---|
paymentMethod | string | Specifies the payment method you want to pre-select for your customer. |
hideOtherPaymentMethods | boolean | Hides other payment methods. Use if you want your customers to pay only using your pre-selected payment method. |
The following example sets paymentMethod
to paypal
and hideOtherPaymentMethods
to true
. Customers would only see PayPal as a payment method during checkout:
fastspring.builder.secure(
{
"contact": {
"email":"[email protected]",
"firstName":"John",
"lastName":"Doe"
},
"items":
[
{
"product": "example-product",
"quantity": 1,
"pricing": {
"price": {
"USD": 19.00
}
}
}
],
"paymentMethod": "paypal",
"hideOtherPaymentMethods": true
}
);
Pass the Encrypted Payload
There are 2 ways to pass the encrypted payload:
- fscSession: Apply the data when the page loads.
<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.7/fastspring-builder.min.js"" type="text/javascript"
...
- fastspring.builder.secure(): Send the request.
<script>
fastspring.builder.secure(securedData, secureKey);
</script>
Pass a Secure Request in Test Mode
If you are placing a test order, you do not need to encrypt private information. This allows you to troubleshoot and adapt each payload and encryption.
<script>
Fastspring.builder.secure(nonEncryptedJSON, '');
</script>
Accepted Fields
The following tables display the fields which can be securely encrypted. Required fields will vary depending on what you include in your JSON string.
Contact Information
Field | Data Type | Description |
---|---|---|
contact | object | Customer contact information. This is required if you pass accountCustomKey. The SBL does not prompt the customer to enter this information at checkout. |
firstName | string | Customer’s first name. Required when passing contact information. |
lastName | string | Customer’s last name. Required when passing contact information. |
string | Customer’s email address. Required when passing contact information. | |
company | string | Customer’s company name. Required if the storefront requires it. |
address | object | Customer address information. |
addressLine1 | string | Line 1 of the customer’s mailing address. Required if the order includes physical fulfillments. |
addressLine2 | string | Line 2 of the customer’s mailing address. |
city | string | The city in the customer’s address. Required if the order includes physical fulfillments. |
region | string | 2-character ISO region code of the associated US state. Required if the order includes physical fulfillments, and the recipient resides in the US. |
postalCode | string | Customer’s postal code. Required for credit card transactions. |
phoneNumber | string | Customer’s phone number. Required if the storefront requires it. |
country | string | 2-character ISO country code. Required if the order includes physical fulfillments. |
accountCustomKey | string | The Account ID in your system. If you pass this string, the contact object is required. |
account | string | FastSpring-generated Account ID (optional). |
taxId | string | The customer’s VAT ID or GST ID. FastSpring will validate it in conjunction with the IP address country. If valid, tax exemption is applied. This field is optional. |
Example Request Payload
{
contact: { // customer details - optional unless you pass accountCustomKey (below); if customer contact info is passed, customer will not be prompted to provide it during checkout
firstName: "firstName" // customer first name - required when passing contact info
lastName: "lastName" // customer last name - required when passing contact info
email: "[email protected]" // customer email address - required when passing contact info
company: "Acme Inc" // customer company (optional unless required by Storefront settings)
addressLine1: "Address Line 1"// customer address line 1 (optional unless order includes physical product or 'Force phsyical address collection' is enabled in Storefront settings)
addressLine2: "Address Line 2"// customer address line 2 (optional)
city: "City" // customer city (optional unless order includes physical product or 'Force physical address collection' is enabled in Storefront settings)
region: "California" // 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: "93101" // customer postal code (required for credit card transactions)
phoneNumber: "800-555-1212" // customer phone number (optional unless required by Store settings)
country: "US" // customer country - two-character ISO country code (optional unless order includes physical product or 'Force physical address collection' enabled in Storefront settings)
},
accountCustomKey: "123abc", // optional account ID from your system; if you pass this then the contact object above is required
account: "x2345adsdadq0zzz", // FastSpring-generated customer account ID (optional)
taxId: "234958723945", // 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
}
Product Information
Field | Data Type | Description |
---|---|---|
items | object | Lists the product information for each product in the session. |
product | string | Product ID that is added to the cart. |
quantity | integer | Quantity of the product being added to the cart. |
pricing | object | If you pass a “pricing” object, define all applicable pricing options. |
trial | integer | Number of free trial days. Required for subscriptions. |
interval | string | Billing frequency. "adhoc", "day", "week", "month", or "year". |
intervalLength | integer | Number of billings per intervalLength period. |
intervalCount | integer | Number of billings until subscription expires. Input null for unlimited billings. |
quantityBehavior | string | Display and behavior of the quantity field. "allow", "lock", or "hide" the quantity field from customers at checkout. |
quantityDefault | integer | Default product quantity. Required if you do not pass the quantity string above. |
price | object | Price of the product. You can pass multiple currencies. Example: { “USD”: 5.0, “EUR”: 4.0 }. |
discountType | string | Type of discount to be applied to the product. "percent" or "amount". |
quantityDiscounts | object | Specify the quantity required to receive a product discount. Then, enter the discount amount or percent. Example (percent): { 2: 10.0 } Example (amount): { 2: {“USD”,5.0} } |
discountReason | object | Customer-facing reason for the discount. Example: {“en”: “English Text”, “de”: “German Text” } |
discountDuration | integer | Number of subscription billings to which the discount applies. |
dateLimitsEnabled | boolean | Allows a date range in which the discount is valid. |
startDate | string | Earliest date that the discount applies. |
endDate | string | Expiration date of the discount. |
startTime | string | Earliest time that the discount applies. Specify in UTC. |
endTime | string | Discount expiration time. Specify in UTC. |
reminder_enabled | boolean | Enable or disable subscription payment reminder notifications. |
reminder_value | string | Interval unit for subscription payment reminder email messages . "day", "week", "month", or "year". |
reminder_count | integer | Number of interval units prior to the next billing date when subscription payment reminder email messages will be sent. |
payment_overdue | boolean | Enable or disable subscription payment overdue notifications. |
overdue_interval_value | integer | interval unit for subscription payment overdue notification email messages. |
overdue_interval_count | integer | Total number of subscription payment overdue notifications that FastSpring sends prior to canceling the subscription. |
overdue_interval_amount | integer | Number of overdue_interval units between each subscription payment overdue notification message. |
cancellation_interval_count | integer | Number of cancellation_interval units prior to subscription cancellation. |
cancellation_interval_value | string | Interval unit for subscription cancellation (subscription only). "day", "week", "month", or "year". |
display | object | Customer-facing product display name. Example: {“en”: “English Display”, “de”: “German Display” } |
description | object | Customer-facing product description. Example: {“en”: “English Description”, “de”: “German Description” } |
image | string | URL of the product icon image. |
selected | boolean | Control whether or not the selected product appears in the cart. |
removeable | boolean | Control whether or not customers can remove the item from their cart. |
attributes | object | Product attributes. |
sku | string | Product SKU ID |
tags | object | Order tags. |
Example Request Payload
{
items: [
{
product: "product-path", // id of the product you are adding to cart
quantity: 1, // quantity of the current product to be added to the cart; ignored if "quantityBehavior" = "lock" or "hide"; in that case, use "quantityDefault" instead
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: 30, // number of free trial days for a subscription (required for subscription only)
interval: "month", // interval unit for scheduled billings; "adhoc" = managed / ad hoc subscription (required for subscription only)
intervalLength: 1, // 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: null, // total number of billings; pass null for unlimited / until cancellation subscription (required for subscription only)
quantityBehavior: "allow", // "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: 1, // default quantity for the product (only needed when "quantityBehavior" = "lock" or "hide", or if you do NOT pass quantity above)
price: { // multiple currencies can be passed
"USD": 5.0,
"EUR": 4.0
},
discountType: "percent", // 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
1: { // 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
"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
"EUR": 4.0
}
},
discountReason: { // reason for the discount, which can optionally be displayed to customers; specified language must be enabled in Store settings
"DE": "German text",
},
discountDuration: 1, // 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: true, // controls whether or not the discount is valid only during a certain date range
startDate:"2023-01-01" , // earliest date on which the discount applies
endDate: "2023-12-31", // discount end / expiration date
startTime: "00:00", // time on the earliest date when discount becomes available; specify time in UTC
endTime: "23:59", // time on the end / expiration date when discount is no longer available; specify time in UTC
reminder_enabled: true, // controls whether or not payment reminder email messages are enabled for the product (subscription only)
reminder_value: "day", // interval unit for payment reminder email messages (subscription only)
reminder_count: 1, // number of interval units prior to the next billing date when payment reminder email messages will be sent (subscription only)
payment_overdue: true, // controls whether or not payment overdue notification email messages are enabled for the product (subscription only)
overdue_interval_value: "day", // interval unit for payment overdue notification email messages (subscription only)
overdue_interval_count: 1, // total number of payment overdue notification messages to send (subscription only)
overdue_interval_amount: 3, // number of overdue_interval units between each payment overdue notification message (subscription only)
cancellation_interval_count: 1, // number of cancellation_interval units prior to subscription cancellation (subscription only)
cancellation_interval_value: "day", // interval unit for subscription cancellation (subscription only)
},
display: { // customer-visible product display name or title
"DE": "German text", // multiple languages supported, language must be enabled in Store settings
},
description: { // customer-visible product description
"values": {
"full": {
"values": {
"en": "English description", // multiple languages supported, language must be enabled in Store settings
}
}
}
},
image: "www.example.com/image", // product icon image file URL
selected: true, // controls whether or not product is currently selected / in the cart
removable: true, // controls whether or not product can be removed from the cart by the customer
attributes: {
key: "value", // optional product attributes (maximum of approximately 400,000 characters)
},
sku: "internal-product-sku-1234" // optional product SKU ID (e.g. to match your internal SKU or product ID)
},
],
tags: { // optional order tags (maximum approximately 4,000 characters)
key: "value",
}
}