Microform Integration
replaces the primary account number (PAN) or card verification number (CVN) field, or both, in your payment input form. It has two components:
  • Server-side component to create a capture context request that contains limited-use public keys from the
    Flex API
    v2 suite.
  • Client-side JavaScript library that you integrate into your digital payment acceptance web page for the secure acceptance of payment information.
Version Numbering
Microform Integration
follows Semantic Versioning.
Cybersource
recommends referencing the latest major version, v2, to receive the latest patch and minor versions automatically. Referencing a specific patch version is not supported.
Upgrade Paths
Because of semantic versioning, every effort will be made to ensure that upgrade paths and patch releases are backwards-compatible and require no code change.

Creating the Server-Side Context

The first step in integrating with
Microform Integration
is developing the server-side code that generates the capture context. The capture context is a digitally signed JWT that provides authentication, one-time keys, and the target origin to the
Microform Integration
application. The target origin is the protocol, URL, and port number (if used) of the page on which you will host the microform. You must use the
https://
protocol unless you use
http://localhost
. For example, if you are serving Microform on example.com, the target origin is
https://example.com.
You can also configure microform to filter out cards by designating the accepted card types.
Sample
Microform Integration
projects are available for download in the Flex samples on GitHub.
  1. Send an authenticated POST request to
    https://apitest.cybersource.com
    /microform/v2/sessions
    . Include the target origin URL and at least one accepted card type in the content of the body of the request.

    ADDITIONAL INFORMATION

    For example:
    { "targetOrigins": ["https://www.example.com"], "allowedCardNetworks": ["VISA"], "clientVersion": "v2.0" }

    ADDITIONAL INFORMATION

    Optionally, you can include multiple target origins and a list of your accepted card types. For example:
    { "targetOrigins": ["https://www.example.com", "https://www.example.net"] "allowedCardNetworks": ["VISA", "MAESTRO", "MASTERCARD", "AMEX", "DISCOVER", "DINERSCLUB", "JCB", "CUP", "CARTESBANCAIRES", "CARNET" ], "clientVersion": "v2.0" }
  2. Pass the capture context response data object to your front-end application. The capture context is valid for 15 minutes.

    ADDITIONAL INFORMATION

AFTER COMPLETING THE TASK

Important Security Note:
  • Ensure that all endpoints within your ownership are secure with some kind of authentication so they cannot be called at will by bad actors.
  • Do not pass the
    targetOrigin
    in any external requests. Hard code it on the server side.

Validating the Capture Context

The capture context that you generated is a JSON Web Token (JWT) data object. The JWT is digitally signed using a public key. The purpose is to ensure the validity of the JWT and confirm that it comes from
Cybersource
. When you do not have a key specified locally in the JWT header, you should follow best cryptography practices and validate the capture context signature.
To validate a JWT, you can obtain its public key. This public RSA key is in JSON Web Key (JWK) format. This public key is associated with the capture context on the
Cybersource
domain.
To get the public key of a capture context from the header of the capture context itself, retrieve the key ID associated with the public key. Then, pass the key ID to the
public-keys
endpoint.
Example
From the header of the capture context, get the key ID (
kid
) as shown in this example:
{
"kid": "3g"
, "alg": "RS256" }
Append the key ID to the endpoint
/flex/v2/public-keys/
3g
. Then, call this endpoint to get the public key.
IMPORTANT
When validating the public key, some cryptographic methods require you to convert the public key to PEM format.

Resource

Pass the key ID (kid), that you obtained from the capture context header, as a path parameter, and send a GET request to the
/public-keys
endpoint:
  • Test:
    https://apitest.cybersource.com
    /flex/v2/public-keys/
    {kid}
  • Production:
    https://api.cybersource.com
    /flex/v2/public-keys/
    {kid}
The resource returns the public key. Use this public RSA key to validate the capture context.

Example

eyJraWQiOiIzZyIsImFsZyI6IlJTMjU2In0.eyJmbHgiOnsicGF0aCI6Ii9mbGV4L3YyL3Rva2VucyIsImRhdGEiOiI2bUFLNTNPNVpGTUk5Y3RobWZmd2doQUFFRGNqNU5QYzcxelErbm8reDN6WStLOTVWQ2c5bThmQWs4czlTRXBtT21zMmVhbEx5NkhHZ29oQ0JEWjVlN3ZUSGQ5YTR5a2tNRDlNVHhqK3ZoWXVDUmRDaDhVY1dwVUNZWlZnbTE1UXVFMkEiLCJvcmlnaW4iOiJodHRwczovL3Rlc3RmbGV4LmN5YmVyc291cmNlLmNvbSIsImp3ayI6eyJrdHkiOiJSU0EiLCJlIjoiQVFBQiIsInVzZSI6ImVuYyIsIm4iOiJyQmZwdDRjeGlkcVZwT0pmVTlJQXcwU1JCNUZqN0xMZjA4U0R0VmNyUjlaajA2bEYwTVc1aUpZb3F6R3ROdnBIMnFZbFN6LVRsSDdybVNTUEZIeTFJQ3BfZ0I3eURjQnJ0RWNEanpLeVNZSTVCVjNsNHh6Qk5CNzRJdnB2Smtqcnd3QVZvVU4wM1RaT3FVc0pfSy1jT0xpYzVXV0ZhQTEyOUthWFZrZFd3N3c3LVBLdnMwNmpjeGwyV05STUIzTS1ZQ0xOb3FCdkdCSk5oYy1uM1lBNU5hazB2NDdiYUswYWdHQXRfWEZ0ZGItZkphVUVUTW5WdW9fQmRhVm90d1NqUFNaOHFMOGkzWUdmemp2MURDTUM2WURZRzlmX0tqNzJjTi1OaG9BRURWUlZyTUtiZ3QyRDlwWkJ1d2gzZlNfS3VRclFWTVdPelRnT3AzT2s3UVFGZ1EiLCJraWQiOiIwOEJhWXMxbjdKTUhjSDh1bkcxc1NDUVdxN2VveWQ1ZyJ9fSwiY3R4IjpbeyJkYXRhIjp7InRhcmdldE9yaWdpbnMiOlsiaHR0cHM6Ly93d3cudGVzdC5jb20iXSwibWZPcmlnaW4iOiJodHRwczovL3Rlc3RmbGV4LmN5YmVyc291cmNlLmNvbSJ9LCJ0eXBlIjoibWYtMC4xMS4wIn1dLCJpc3MiOiJGbGV4IEFQSSIsImV4cCI6MTYxNjc3OTA5MSwiaWF0IjoxNjE2Nzc4MTkxLCJqdGkiOiJ6SG1tZ25uaTVoN3ptdGY0In0.GvBzyw6JKl3b2PztHb9rZXawx2T817nYqu6goxpe4PsjqBY1qeTo19R-CP_DkJXov9hdJZgdlzlNmRY6yoiziSZnGJdpnZ-pCqIlC06qrpJVEDob3O_efR9L03Gz7F5JlLOiTXSj6nVwC5mRlcP032ytPDEx5TMI9Y0hmBadJYnhEMwQnn_paMm3wLh2v6rfTkaBqd8n6rPvCNrWMOwoMdoTeFxku-d27jlA95RXqJWfhJSN1MFquKa7THemvTX2tnjZdTcrTcpgHlxi22w7MUFcnNXsbMouoaYiEdAdSlCZ7LCXrS1Brdr_FWDp7v0uwqHm7OALsGrw8QbGTafF8w
Base64 decode the capture context to get the key ID (
kid
) from its header:
{
"kid": "3g"
, "alg": "RS256" }
Get its public key from
/flex/v2/public-keys/3g
:
{ "kty":"RSA", "use":"enc", "kid":"3g", "n":"ir7Nl1Bj8G9rxr3co5v_JLkP3o9UxXZRX1LIZFZeckguEf7Gdt5kGFFfTsymKBesm3Pe 8o1hwfkq7KmJZEZSuDbiJSZvFBZycK2pEeBjycahw9CqOweM7aKG2F_bhwVHrY4YdKsp _cSJe_ZMXFUqYmjk7D0p7clX6CmR1QgMl41Ajb7NHI23uOWL7PyfJQwP1X8HdunE6ZwK DNcavqxOW5VuW6nfsGvtygKQxjeHrI-gpyMXF0e_PeVpUIG0KVjmb5-em_Vd2SbyPNme nADGJGCmECYMgL5hEvnTuyAybwgVwuM9amyfFqIbRcrAIzclT4jQBeZFwkzZfQF7MgA6QQ", "e":"AQAB" }

Setting Up the Client Side

You can integrate
Microform Integration
with your native payment acceptance web page or mobile application.

Web Page

Initiate and embed
Microform Integration
into your payment acceptance web page.
  1. Add the
    Microform Integration
    JavaScript library to your page by loading it directly from
    Cybersource
    . See Version Numbering. You should do this dynamically per environment by using the asset path returned in the JWT from
    /microform/v2/sessions
    . For example:

    ADDITIONAL INFORMATION

    ctx": [ { "data": { "clientLibrary": https://testflex.cybersource.com/microform/bundle/v2/flex-microform.min.js, ...
    • Test
      :
      <script src="https://testflex.cybersource.com/microform/bundle/v2/flex-microform.min.js"></script>
    • Production
      :
      <script src="https://flex.cybersource.com/microform/bundle/v2/flex-microform.min.js"></script>
  2. Create the HTML placeholder objects to attach to the microforms.

    ADDITIONAL INFORMATION

    Microform Integration
    attaches the microform fields to containers within your HTML. Within your HTML checkout, replace the payment card and CVN tag with a simple container.
    Microform Integration
    uses the container to render an iframe for secured credit card input. The following example contains simple
    div
    tags to define where to place the PAN and CVN fields within the payment acceptance page:
    <div id="number-container" class="form-control"></div>
    . See Example: Checkout Payment Form.
  3. Invoke the Flex SDK by passing the capture context that was generated in the previous step to the microform object.

    ADDITIONAL INFORMATION

    var flex = new Flex(captureContext);
  4. Initiate the microform object with styling to match your web page.

    ADDITIONAL INFORMATION

    After you create a new Flex object, you can begin creating your Microform. You will pass your baseline styles and ensure that the button matches your merchant page.
    var microform = flex.microform({ styles: myStyles });
  5. Create and attach the microform fields to the HTML objects through the Microform Integration JavaScript library.

    ADDITIONAL INFORMATION

    var number = microform.createField('number', { placeholder: 'Enter card number' }); var securityCode = microform.createField('securityCode', { placeholder: '•••' }); number.load('#number-container'); securityCode.load('#securityCode-container');
  6. Create a function for the customer to submit their payment information, and invoke the tokenization request to
    Microform Integration
    for the transient token.

Mobile Application

To initiate and embed
Microform Integration
into native payment acceptance mobile application, follow the steps for web page setup, and ensure that these additional requirements are met:
  • The card acceptance fields of PAN and CVV must be hosted on a web page.
  • The native application must load the hosted card entry form web page in a web view.

AFTER COMPLETING THE TASK

As an alternative, you can use the Mobile SDKs hosted on GitHub:

Transient Token Time Limit

Transient Token Time Limit
The sensitive data associated with the transient token is available for use only for 15 minutes or until one successful authorization occurs. Before the transient token expires, its data is still usable in other non-authorization services. After 15 minutes, you must prompt the customer to restart the checkout flow.
When the customer submits the form,
Microform Integration
securely collects and tokenizes the data in the loaded fields as well as the options supplied to the
createToken()
function. The month and year are included in the request. If tokenization succeeds, your callback receives the token as its second parameter. Send the token to your server, and use it in place of the PAN when you use supported payment services.

Transient Token Response Format

The transient token is issued as a JSON Web Token (RFC 7519). A JWT is a string consisting of three parts that are separated by dots:
  • Header
  • Payload
  • Signature
JWT example:
xxxxx.yyyyy.zzzzz
The payload portion of the token is an encoded Base64url JSON string and contains various claims.
IMPORTANT
The internal data structure of the JWT can expand to contain additional data elements. Ensure that your integration and validation rules do not limit the data elements contained in responses.

Validating the Transient Token

After receiving the transient token, validate its integrity using the public key embedded within the capture context created at the beginning of this flow. This verifies that
Cybersource
issued the token and that no data tampering occurred during transit. See Example: Capture Context Public Key.
Use the capture context public key to cryptographically validate the JWT provided from a successful
microform.createToken
call. You might have to convert the JSON Web Key (JWK) to privacy-enhanced mail (PEM) format for compatibility with some JWT validation software libraries.
The
Cybersource
SDK has functions that verify the token response. You must verify the response to ensure that no tampering occurs as it passes through the cardholder device. Do so by using the public key generated at the start of the process.

Using the Transient Token

After you validate the transient token, you can use it in place of the PAN with payment services for 15 minutes. See Transient Token Time Limit.
When the consuming service receives a request containing a transient token, it retrieves the tokenized data and injects the values into your request before processing, and none of the sensitive data is stored on your systems. In some scenarios, the
jti
value contained in the JWT transient token response must be extracted and used instead of the entire JWT.
Connection Method
Field
Simple Order API
tokenSource_transientToken
SCMP API
transient_token
REST API with Transient Token JSON Web Token
"tokenInformation": {
"transientTokenJwt": "eyJraWQiOiIwNzRsM3p5M2xCRWN5d1gxcnhXNFFoUmJFNXJLN1NmQiIsImFsZyI6IlJTMjU2In0.eyJkYXRhIjp7ImV4cGlyYXRpb25ZZWFyIjoiMjAyMSIsIm51bWJlciI6IjQxMTExMVhYWFhYWDExMTEiLCJleHBpcmF0aW9uTW9udGgiOiIwNSIsInR5cGUiOiIwMDEifSwiaXNzIjoiRmxleC8wOCIsImV4cCI6MTU4ODcwMjkxNSwidHlwZSI6Im1mLTAuMTEuMCIsImlhdCI6MTU4ODcwMjAxNSwianRpIjoiMUU0Q0NMSUw4NFFXM1RPSTFBM0pUU1RGMTZGQUNVNkUwNU9VRVNGWlRQNUhIVkJDWTQwUTVFQjFBRUMzNDZBMCJ9.FB3b2r8mjtvqo3_k05sRIPGmCZ_5dRSZp8AIJ4u7NKb8E0-6ZOHDwEpxtOMFzfozwXMTJ3C6yBK9vFIPTIG6kydcrWNheE2Pfort8KbxyUxG-PYONY-xFnRDF841EFhCMC4nRFvXEIvlcLnSK6opUUe7myKPjpZI1ijWpF0N-DzZiVT8JX-9ZIarJq2OI0S61Y3912xLJUKi5c2VpRPQOS54hRr5GHdGJ2fV8JZ1gTuup_qLyyK7uE1VxI0aucsyH7yeF5vTdjgSd76ZJ1OUFi-3Ij5kSLsiX4j-D0T8ENT1DbB_hPTaK9o6qqtGJs7QEeW8abtnKFsTwVGrT32G2w"
}
REST API with JSON Web Token ID
"tokenInformation": {
    "jti": "1E3GQY1RNKBG6IBD2EP93C43PIZ2NQ6SQLUIM3S16BGLHTY4IIEK5EB1AE5D73A4",    
}

Getting Started Examples

Example: Node.js REST Code Snippet

try { var instance = new
cybersourceRestApi
smartpayFuseRestApi
.KeyGenerationApi(configObj); var request = new
cybersourceRestApi
smartpayFuseRestApi
.GeneratePublicKeyRequest(); request.encryptionType = 'RsaOaep256'; request.targetOrigin = 'http://localhost:3000'; var opts = []; opts['format'] = 'JWT'; console.log('\n*************** Generate Key ********************* '); instance.generatePublicKey(request, opts, function (error, data, response) { if (error) { console.log('Error : ' + error); console.log('Error status code : ' + error.statusCode); } else if (data) { console.log('Data : ' + JSON.stringify(data)); console.log('CaptureContext: '+data.keyId); res.render('index', { keyInfo: JSON.stringify(data.keyId)}); } console.log('Response : ' + JSON.stringify(response)); console.log('Response Code Of GenerateKey : ' + response['status']); callback(error, data); }); } catch (error) { console.log(error); }

Example: Checkout Payment Form

This simple payment form captures the name, PAN, CVN, month, and year, and a pay button for submitting the information.
<h1>Checkout</h1> <div id="errors-output" role="alert"></div> <form action="/token" id="my-sample-form" method="post"> <div class="form-group"> <label for="cardholderName">Name</label> <input id="cardholderName" class="form-control" name="cardholderName" placeholder="Name on the card"> <label id="cardNumber-label">Card Number</label> <div id="number-container" class="form-control"></div> <label for="securityCode-container">Security Code</label> <div id="securityCode-container" class="form-control"></div> </div> <div class="form-row"> <div class="form-group col-md-6"> <label for="expMonth">Expiry month</label> <select id="expMonth" class="form-control"> <option>01</option> <option>02</option> <option>03</option> <option>04</option> <option>05</option> <option>06</option> <option>07</option> <option>08</option> <option>09</option> <option>10</option> <option>11</option> <option>12</option> </select> </div> <div class="form-group col-md-6"> <label for="expYear">Expiry year</label> <select id="expYear" class="form-control"> <option>2021</option> <option>2022</option> <option>2023</option> </select> </div> </div> <button type="button" id="pay-button" class="btn btn-primary">Pay</button> <input type="hidden" id="flexresponse" name="flexresponse"> </form>

Example: Creating the Pay Button with Event Listener

payButton.addEventListener('click', function() { // Compiling MM & YY into optional parameters var options = { expirationMonth: document.querySelector('#expMonth').value, expirationYear: document.querySelector('#expYear').value }; // microform.createToken(options, function (err, token) { if (err) { // handle error console.error(err); errorsOutput.textContent = err.message; } else { // At this point you may pass the token back to your server as you wish. // In this example we append a hidden input to the form and submit it. console.log(JSON.stringify(token)); flexResponse.value = JSON.stringify(token); form.submit(); } }); });

Example: Customer-Submitted Form

<script> // Variables from the HTML form var form = document.querySelector('#my-sample-form'); var payButton = document.querySelector('#pay-button'); var flexResponse = document.querySelector('#flexresponse'); var expMonth = document.querySelector('#expMonth'); var expYear = document.querySelector('#expYear'); var errorsOutput = document.querySelector('#errors-output'); // the capture context that was requested server-side for this transaction var captureContext = <%-keyInfo%> ; // custom styles that will be applied to each field we create using Microform var myStyles = { 'input': { 'font-size': '14px', 'font-family': 'helvetica, tahoma, calibri, sans-serif', 'color': '#555' }, ':focus': { 'color': 'blue' }, ':disabled': { 'cursor': 'not-allowed' }, 'valid': { 'color': '#3c763d' }, 'invalid': { 'color': '#a94442' } }; // setup Microform var flex = new Flex(captureContext); var microform = flex.microform({ styles: myStyles }); var number = microform.createField('number', { placeholder: 'Enter card number' }); var securityCode = microform.createField('securityCode', { placeholder: '•••' }); number.load('#number-container'); securityCode.load('#securityCode-container'); // Configuring a Listener for the Pay button payButton.addEventListener('click', function() { // Compiling MM & YY into optional paramiters var options = { expirationMonth: document.querySelector('#expMonth').value, expirationYear: document.querySelector('#expYear').value }; // microform.createToken(options, function (err, token) { if (err) { // handle error console.error(err); errorsOutput.textContent = err.message; } else { // At this point you may pass the token back to your server as you wish. // In this example we append a hidden input to the form and submit it. console.log(JSON.stringify(token)); flexResponse.value = JSON.stringify(token); form.submit(); } }); }); </script>

Example: Token Payload

{ // token id to be used with
Cybersource
services "jti": "408H4LHTRUSHXQZWLKDIN22ROVXJFLU6VLU00ZWL8PYJOZQWGPS9CUWNASNR59K4", // when the token was issued "iat": 1558612859, // when the token will expire "exp": 1558613759, // info about the stored data associated with this token // any sensitive data will be masked "data": { "number": "444433XXXXXX1111", "type": "001", "expirationMonth": "06", "expirationYear": "2025" } }

Example: Token Payload with Multiple Card Types

{ "iss": "Flex/08", "exp": 1661350495, "type": "mf-2.0.0", "iat": 1661349595, "jti": "1C174LLWIFFR9OV0V0IJQOY0IB1JQP70ZNF4TBI3V6H3AIOY0W1T6306325F91C0", "content": { "paymentInformation": { "card": { "expirationYear": { "value": "2023" }, "number": { "detectedCardTypes": [ "042", "036" ], "maskedValue": "XXXXXXXXXXXX1800", "bin": "501767" }, "securityCode": {}, "expirationMonth": { "value": "01" } } } } }

Example: Capture Context Public Key

"jwk": { "kty": "RSA", "e": "AQAB", "use": "enc", "n": "3DhDtIHLxsbsSygEAG1hcFqnw64khTIZ6w9W9mZNl83gIyj1FVk-H5GDMa85e8RZFxUwgU_zQ0kHLtONo8SB52Z0hsJVE9wqHNIRoloiNPGPQYVXQZw2S1BSPxBtCEjA5x_-bcG6aeJdsz_cAE7OrIYkJa5Fphg9_pxgYRod6JCFjgdHj0iDSQxtBsmtxagAGHjDhW7UoiIig71SN-f-gggaCpITem4zlb5kkRVvmKMUANe4B36v4XSSSpwdP_H5kv4JDz_cVlp_Vy8T3AfAbCtROyRyH9iH1Z-4Yy6T5hb-9y3IPD8vlc8E3JQ4qt6U46EeiKPH4KtcdokMPjqiuQ", "kid": "00UaBe20jy9VkwZUQPZwNNoKFPJA4Qhc" }

Example: Validating the Transient Token

This example shows how to extract the signature key from the capture context and use the key to validate the transient token object returned from a successful microform interaction.
console.log('Response TransientToken: ' + req.body.transientToken); console.log('Response CaptureContext: ' + req.body.captureContext); // Validating Token JWT Against Signature in Capture Context var capturecontext = req.body.captureContext; var transientToken = req.body.transientToken; // Extracting JWK in Body of Capture Context var ccBody = capturecontext.split('.')[1]; console.log('Body: ' + ccBody); var atob = require('atob'); var ccDecodedValue = JSON.parse( atob(ccBody)); var jwk = ccDecodedValue.flx.jwk; console.log('CaptureContext JWK: ' + JSON.stringify(jwk)); // Converting JWK to PEM var jwkToPem = require('jwk-to-pem'), jwt = require('jsonwebtoken'); var pem = jwkToPem(jwk); // Validating JWT var validJWT = jwt.verify(transientToken, pem); console.log('Validated Resposonse: ' + JSON.stringify(validJWT));

Example: Authorization with a Transient Token Using the REST API

{ "clientReferenceInformation": { "code": "TC50171_3" }, "orderInformation": { "amountDetails": { "totalAmount": "102.21", "currency": "USD" }, "billTo": { "firstName": "Tanya", "lastName": "Lee", "address1": "1234 Main St.", "locality": "Small Town", "administrativeArea": "MI", "postalCode": "98765-4321", "country": "US", "district": "MI", "buildingNumber": "123", "email": "tanyalee@example.com", "phoneNumber": "987-654-3210" } }, "tokenInformation": { "transientTokenJwt": "eyJraWQiOiIwN0JwSE9abkhJM3c3UVAycmhNZkhuWE9XQlhwa1ZHTiIsImFsZyI6IlJTMjU2In0.eyJkYXRhIjp7ImV4cGlyYXRpb25ZZWFyIjoiMjAyMCIsIm51bWJlciI6IjQxMTExMVhYWFhYWDExMTEiLCJleHBpcmF0aW9uTW9udGgiOiIxMCIsInR5cGUiOiIwMDEifSwiaXNzIjoiRmxleC8wNyIsImV4cCI6MTU5MTc0NjAyNCwidHlwZSI6Im1mLTAuMTEuMCIsImlhdCI6MTU5MTc0NTEyNCwianRpIjoiMUMzWjdUTkpaVjI4OVM5MTdQM0JHSFM1T0ZQNFNBRERCUUtKMFFKMzMzOEhRR0MwWTg0QjVFRTAxREU4NEZDQiJ9.cfwzUMJf115K2T9-wE_A_k2jZptXlovls8-fKY0muO8YzGatE5fu9r6aC4q7n0YOvEU6G7XdH4ASG32mWnYu-kKlqN4IY_cquRJeUvV89ZPZ5WTttyrgVH17LSTE2EvwMawKNYnjh0lJwqYJ51cLnJiVlyqTdEAv3DJ3vInXP1YeQjLX5_vF-OWEuZfJxahHfUdsjeGhGaaOGVMUZJSkzpTu9zDLTvpb1px3WGGPu8FcHoxrcCGGpcKk456AZgYMBSHNjr-pPkRr3Dnd7XgNF6shfzIPbcXeWDYPTpS4PNY8ZsWKx8nFQIeROMWCSxIZOmu3Wt71KN9iK6DfOPro7w" } }