braintree-web

repository·main·Indexed 19 days ago

https://github.com/braintree/braintree-web

A suite of JavaScript tools for integrating Braintree payment services into web applications. It provides features such as Hosted Fields for secure payment data collection, a client for communicating with Braintree servers, and support for both callbacks and Promises for asynchronous operations.

Tokens
30K
Snippets
88
Records
130
Agent score
65%

What's inside braintree-web

  1. Use Promises with braintree-web

    main

    All asynchronous methods in the Braintree SDK return a Promise if no callback is provided. This allows for cleaner async/await or .then() chains.

    braintree.client
      .create({ authorization: CLIENT_AUTHORIZATION })
      .then(function (client) {
        return braintree.hostedFields.create({
          client: client,
          styles: yourStylesConfig,
          fields: yourFieldsConfig,
        });
      })
      .then(function (hostedFields) {
        return hostedFields.tokenize();
      })
      .then(function (payload) {
        // send payload.nonce to your server
      })
      .catch(function (err) {
        console.error(err);
      });
  2. Use callbacks or Promises with Braintree Web

    main

    All asynchronous methods in the Braintree SDK support both Node.js-style callbacks and Promises.

    Callbacks

    Pass a callback function as the last argument. The callback receives err as the first parameter and the returned data as the second.

    Promises

    If no callback is provided, the method returns a Promise. This is the recommended approach for modern JavaScript applications.

    // Using Callbacks
    braintree.client.create({...}, function (err, clientInstance) {
      if (err) { /* handle error */ }
      // use clientInstance
    });
    
    // Using Promises
    braintree.client
      .create({
        authorization: CLIENT_AUTHORIZATION,
      })
      .then(function (client) {
        // Create other components
      })
      .catch(function (err) {
        // handle error
      });
  3. How Braintree Web modules and hierarchy work

    main

    The Braintree JavaScript SDK is organized into standalone modules, where each module is represented by a class that encapsulates specific functionality. To use a feature, you call the module's create function, which returns an instance of that module's class.

    Many modules (such as paypal or hostedFields) depend on a Client instance to communicate with Braintree servers. You can create a single Client instance and pass it to the create methods of multiple other modules to share the underlying communication layer.

    // Example of the dependency pattern:
    // braintree.client.create(...) --------> Client
    // braintree.paypal.create(...) --------> PayPal (uses Client)
    // braintree.hostedFields.create(...) --> HostedFields (uses Client)
  4. Build the braintree-web SDK

    main

    You can build the entire SDK or specific components using npm scripts. Building generates two types of output in the dist/ directory:

    1. dist/npm/: Contains the pre-processed source tree ready for publication to npm and use in CommonJS environments.
    2. dist/hosted/: Contains assets (js, css, html, images) structured to mirror the production CDN (e.g., https://assets.braintreegateway.com).

    To build all components:

    npm run build

    To build a specific component (replace <component> with the component name, e.g., client or paypal):

    npm run build <component>
    npm run build
    # or
    npm run build <component>
  5. Run tests for braintree-web

    main

    The project uses Jest for testing. You can run tests for the whole suite, specific components, or individual files.

    Run all tests:

    npm test

    Run tests for a specific component:

    npm test <component>

    Run tests for the lib directory:

    npm test lib

    Run a single test file: First, ensure Jest is installed globally:

    npm install jest --global

    Then run the file path directly with Jest:

    jest <path/to/file>
    # Example:
    jest test/apple-pay/unit/apple-pay.js
    npm test
    npm test <component>
    npm test lib
  6. Configure BrowserStack to use HTTPS for ApplePay testing

    main

    To test workflows requiring HTTPS (like ApplePay) on BrowserStack, you must configure BrowserStack to accept self-signed certificates, generate the certificates, and run your test server over HTTPS.

    1. Enable self-signed certificates in WebdriverIO

    Update your wdio.conf.ts to include acceptInsecureCerts: true in your capabilities.

    2. Generate SSL Certificates

    Run the provided script to create local test certificates:

    .storybook/scripts/generate-test-certs.sh

    This generates .storybook/certs/localhost.key and .storybook/certs/localhost.crt.

    3. Implement an HTTPS Test Server

    When setting up your test server, ensure useHttps is set to true and use the https:// protocol for your test URLs.

    // wdio.conf.ts
    capabilities: [
      {
        browserName: "Safari",
        acceptInsecureCerts: true, // Required for self-signed certs
      },
    ];
    
    // Test HTTPS Server Example
    import { createTestServer, type TestServerResult } from "./helper";
    
    let server: http.Server | https.Server;
    let serverPort: number;
    
    beforeEach(async function () {
      const result: TestServerResult = await createTestServer({
        useHttps: true, // Enable HTTPS
      });
      server = result.server;
      serverPort = result.port;
    });
    
    const getTestUrl = (path: string) => {
      return `https://localhost:${serverPort}${path}`;
    };
  7. Execute inline scripts with CSP using hashes or nonces

    main

    To allow inline scripts while maintaining a strict CSP, avoid using 'unsafe-inline'. Instead, use a hash-source or a nonce-source.

    Using Hash-source

    Include a SHA hash of the exact contents of your <script> tag in the script-src directive. Note that any change to the script (including whitespace) will change the hash.

    <html><head><meta http-equiv="Content-Security-Policy" content="
        Content-Security-Policy: script-src 'unsafe-inline' 'sha256-zVu1jtS1MTItvxLN0tAAAAOAOlDFjjz/oAIlo5KIjMs='
    "/><head>
    <script>console.log("execution of inline-script")</script>
    </html>

    Using Nonce-source

    Generate a cryptographically strong, random, one-time use value (at least 128 bits) for each request. Attach this value to both the CSP header and the <script> tag.

    <html><head>
      <meta http-equiv="Content-Security-Policy" content="
        Content-Security-Policy: script-src 'unsafe-inline' 'nonce-123a456b789c000d='
    "
    />
    <head>
    <script nonce="123a456b789c000d=">console.log("execution of inline-script");</script>
    <script nonce="123a456b789c000d=">var sum = 1 + 2;</script>
    </html>
    <html><head><meta http-equiv="Content-Security-Policy" content="
        Content-Security-Policy: script-src 'unsafe-inline' 'sha256-zVu1jtS1MTItvxLN0tAAAAOAOlDFjjz/oAIlo5KIjMs='
    "/><head>
    <script>console.log("execution of inline-script")</script>
    </html>
  8. Integrate Hosted Fields

    main

    Hosted Fields allow you to render payment fields (like card number and CVV) in your own UI while maintaining PCI compliance. You first create a client using braintree.client.create, then use that client to initialize braintree.hostedFields.create with specific styles and fields selectors.

    // 1. Create the client
    braintree.client.create(
      {
        authorization: CLIENT_AUTHORIZATION,
      },
      function(err, client) {
        // 2. Create Hosted Fields
        braintree.hostedFields.create(
          {
            client: client,
            styles: {
              input: { "font-size": "16pt", "color": "#3A3A3A" },
              ".number": { "font-family": "monospace" },
              ".valid": { "color": "green" }
            },
            fields: {
              number: { selector: "#card-number" },
              cvv: { selector: "#cvv" },
              expirationDate: { selector: "#expiration-date" }
            }
          },
          function(err, hostedFields) {
            // 3. Use hostedFields.tokenize() to get a nonce
          }
        );
      }
    );
  9. Release braintree-web assets and documentation

    main

    Use the npm run release command with specific targets to deploy different parts of the project:

    Release hosted assets: Builds and copies files into the directory defined by BRAINTREE_JS_HOSTED_DEST in your .env file.

    npm run release -- hosted

    Release JSDocs: Deploys the generated documentation.

    npm run release -- jsdoc

    Prepare source release: Prepares source changes for the braintree-web repository.

    npm run release -- source
    npm run release -- hosted
    npm run release -- jsdoc
    npm run release -- source
  10. Test local builds in Storybook

    main

    To test changes in your local dist/ directory before they are published to the CDN, follow these steps:

    1. Build the project: npm run build
    2. Run Storybook with local assets enabled: npm run storybook:dev-local
    3. In the Storybook UI, use the version selector dropdown to select "Assets from local build".
    npm run build
    npm run storybook:dev-local
  11. Run integration tests with Browserstack

    main

    Integration testing can be done using published CDN versions (default) or your local development builds.

    Setup

    Ensure your .env contains:

    • BRAINTREE_JS_ENV=development
    • STORYBOOK_BRAINTREE_TOKENIZATION_KEY=<key>
    • BROWSERSTACK_USERNAME=<username>
    • BROWSERSTACK_ACCESS_KEY=<password>

    And generate local SSL certificates:

    openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem -subj "/CN=127.0.0.1"

    Running Tests

    Default (CDN versions):

    npm run test:integration

    Local Builds (Full Workflow): To test local changes, you must run the build and the local server in separate terminals:

    1. Terminal 1: Build and prepare the environment:
      npm run build:integration
    2. Terminal 2: Start the local development server:
      npm run storybook:dev-local
    3. Terminal 3: Run the tests:
      npm run test:integration:local