While the npm packages braintree, square, and stripe are officially server-side Node.js SDKs, frontend architects choose between these providers based on their client-side integration capabilities. This comparison focuses on the associated JavaScript libraries (braintree-web, @square/web-payments-sdk, and @stripe/stripe-js) that run in the browser. We evaluate initialization patterns, UI component flexibility, tokenization flows, and PCI compliance handling to help teams select the right payment ecosystem for their web application.
Choosing a payment provider is one of the most critical architectural decisions in e-commerce development. While the npm packages stripe, braintree, and square are server-side libraries, the frontend experience depends on their client-side counterparts. Let's compare how they handle initialization, UI components, and security in modern web applications.
stripe uses a singleton pattern loaded via a public key.
loadStripe with your publishable key.// stripe: Client-side initialization
import { loadStripe } from '@stripe/stripe-js';
const stripe = await loadStripe('pk_test_...');
braintree requires creating a client token on the server first.
// braintree: Client-side initialization
import braintree from 'braintree-web';
const clientToken = await fetch('/api/payment-token').then(r => r.json());
const clientInstance = await braintree.client.create({ authorization: clientToken });
square initializes with an application ID and location ID.
// square: Client-side initialization
import { WebPaymentsSdk } from '@square/web-payments-sdk';
const payments = await WebPaymentsSdk.initialize({
applicationId: 'sq0idp-...',
locationId: 'L...'
});
stripe offers "Elements" for custom designs.
// stripe: Custom Elements
const elements = stripe.elements();
const cardElement = elements.create('card');
cardElement.mount('#card-element');
braintree offers a "Drop-in" UI for speed.
// braintree: Drop-in UI
import braintreeDropin from 'braintree-web-drop-in';
await braintreeDropin.create({
authorization: clientToken,
container: '#dropin-container'
}, (err, dropinInstance) => { /*...*/ });
square provides a card() method for forms.
// square: Card Component
const card = await payments.card();
await card.attach('#card-container');
stripe confirms payments directly from the client.
confirmCardPayment.// stripe: Confirm Payment
const result = await stripe.confirmCardPayment(clientSecret, {
payment_method: { card: cardElement }
});
braintree requests a payment method nonce.
// braintree: Request Payment Method
const { nonce } = await dropinInstance.requestPaymentMethod();
// Send nonce to your server to charge
square tokenizes card details directly.
tokenize() on the card component.// square: Tokenize
const result = await card.tokenize();
if (result.status === 'OK') {
// Send result.token to your server
}
stripe has official React wrappers.
@stripe/react-stripe-js for context and hooks.CardElement are ready to import.// stripe: React Component
import { CardElement, useStripe } from '@stripe/react-stripe-js';
function CheckoutForm() {
const stripe = useStripe();
return <CardElement />;
}
braintree relies on community wrappers.
useEffect hooks.// braintree: React Usage
useEffect(() => {
braintreeDropin.create({ /*...*/ });
}, []);
square provides official React components.
@square/react-web-payments-sdk.Card and GooglePay components out of the box.// square: React Component
import { Card } from '@square/react-web-payments-sdk';
function Checkout() {
return <Card />;
}
stripe keeps you in SAQ A scope.
// stripe: Security Model
// Inputs are hosted in Stripe iframes
// Your code only handles tokens or PaymentIntent IDs
braintree keeps you in SAQ A scope.
// braintree: Security Model
// Drop-in handles all PCI data
// Server receives only a nonce string
square keeps you in SAQ A scope.
// square: Security Model
// Card component handles secure data entry
// Server receives only a single-use token
| Feature | stripe | braintree | square |
|---|---|---|---|
| UI Flexibility | ⭐⭐⭐ High (Elements) | ⭐ Low (Drop-in) | ⭐⭐ Medium (SDK) |
| React Support | ✅ Official Wrappers | ⚠️ Community Only | ✅ Official Wrappers |
| PayPal Support | ⚠️ Via Extra Config | ✅ Built-in Core | ⚠️ Via Extra Config |
| Setup Complexity | 🟢 Low | 🟡 Medium | 🟢 Low |
| Best For | Custom Brands | PayPal Users | Omnichannel Retail |
stripe is the developer-first choice 🧰. It offers the best documentation, the most flexible UI components, and strong React support. Choose this if your checkout design is a key part of your brand identity.
braintree is the PayPal powerhouse 🅿️. If your customers expect PayPal or you want a secure Drop-in UI with minimal code, this is the strong candidate. It reduces frontend work but limits design control.
square is the retail unifier 🏪. If you operate physical stores with Square terminals, using their web SDK keeps your data and operations in one place. It balances ease of use with decent customization.
Final Thought: All three providers handle PCI compliance securely by isolating sensitive data. Your decision should depend on whether you value design freedom (stripe), PayPal integration (braintree), or omnichannel consistency (square).
Choose braintree if your application requires deep PayPal integration or needs to support a wide range of alternative payment methods out of the box. Its Drop-in UI is highly secure because it uses iframes to isolate sensitive data, reducing your PCI compliance scope significantly. This option works well for teams that prefer a pre-built interface over custom design.
Choose square if your business already uses Square POS hardware and you want a unified experience across online and offline channels. The Web Payments SDK offers strong support for digital wallets like Apple Pay and Google Pay with minimal configuration. It is ideal for retailers who need consistency between their physical store and web presence.
Choose stripe if you prioritize developer experience, extensive documentation, and fully customizable UI components. The Elements library allows you to build forms that match your brand exactly while still handling security compliance for you. This is the best fit for startups and scale-ups that need flexibility and rapid iteration on checkout flows.
The Braintree Node library provides integration access to the Braintree Gateway.
The Payment Card Industry (PCI) Council has mandated that early versions of TLS be retired from service. All organizations that handle credit card information are required to comply with this standard. As part of this obligation, Braintree is updating its services to require TLS 1.2 for all HTTPS connections. Braintree will also require HTTP/1.1 for all connections. Please see our technical documentation for more information.
npm install braintreevar braintree = require('braintree')Braintree employs a deprecation policy for our SDKs. For more information on the statuses of an SDK check our developer docs.
| Major version number | Status | Released | Deprecated | Unsupported |
|---|---|---|---|---|
| 3.x.x | Active | September 2020 | TBA | TBA |
| 2.x.x | Inactive | February 2017 | September 2022 | September 2023 |
Updating from an Inactive, Deprecated, or Unsupported version of this SDK? Check our Migration Guide for tips.
var braintree = require("braintree");
var gateway = new braintree.BraintreeGateway({
environment: braintree.Environment.Sandbox,
merchantId: "your_merchant_id",
publicKey: "your_public_key",
privateKey: "your_private_key",
});
gateway.transaction.sale(
{
amount: "5.00",
paymentMethodNonce: "nonce-from-the-client",
options: {
submitForSettlement: true,
},
},
function (err, result) {
if (err) {
console.error(err);
return;
}
if (result.success) {
console.log("Transaction ID: " + result.transaction.id);
} else {
console.error(result.message);
}
}
);
You can also use Promises instead of callbacks.
var braintree = require("braintree");
var gateway = new braintree.BraintreeGateway({
environment: braintree.Environment.Sandbox,
merchantId: "your_merchant_id",
publicKey: "your_public_key",
privateKey: "your_private_key",
});
gateway.transaction
.sale({
amount: "5.00",
paymentMethodNonce: "nonce-from-the-client",
options: {
submitForSettlement: true,
},
})
.then(function (result) {
if (result.success) {
console.log("Transaction ID: " + result.transaction.id);
} else {
console.error(result.message);
}
})
.catch(function (err) {
console.error(err);
});
Almost all methods that uses a callback can alternatively use a Promise. The only exceptions are gateway.merchantAccount.all or any of the search methods because they return a stream if no callback is provided.
The Makefile and Dockerfile will build an image containing the dependencies and drop you to a terminal where you can run tests.
make
The unit specs can be run by anyone on any system, but the integration specs are meant to be run against a local development server of our gateway code. These integration specs are not meant for public consumption and will likely fail if run on your system. To run unit tests use rake (rake test:unit) or npm (npm test).
See the LICENSE file.