DEV Community

Cover image for How to Use Braintree APIs?
Wanda
Wanda

Posted on • Originally published at apidog.com

How to Use Braintree APIs?

TL;DR

Braintree APIs let you process payments from credit cards, PayPal, Venmo, and digital wallets. Integrate using server-side SDKs (Node, Python, Ruby, etc.), generate client tokens for frontend security, and handle transactions, refunds, and subscriptions. Use Apidog to validate webhook payloads and test integrations against sandbox data before going live.

Try Apidog today


Introduction

Braintree processes billions of dollars in payments for companies like Uber, Airbnb, and GitHub. It supports credit cards, PayPal, Venmo, Apple Pay, Google Pay, and ACH transfers.

Payment APIs require extra care—mistakes can cost real money and damage customer trust.

Braintree offers two integration paths:

  • Drop-in UI (pre-built payment form)
  • Custom UI (full control)

Both use the same server-side APIs for payment processing. This guide covers the backend workflow after a customer clicks “Pay.”

💡 Tip: When building payment integrations, use Apidog to test webhook handlers and validate payment responses. Mock Braintree webhooks locally to ensure your code handles all scenarios before processing real transactions.

Test Braintree webhooks with Apidog - free


Setting up Braintree

Create a Braintree account

Go to braintreepayments.com and create a sandbox account. You’ll receive:

  • Merchant ID: abc123xyz
  • Public Key: def456...
  • Private Key: ghi789...

Store these securely. Never commit your private key to Git.

Braintree Credentials

Install the SDK

Braintree provides server-side SDKs for most languages.

Node.js:

npm install braintree
Enter fullscreen mode Exit fullscreen mode

Python:

pip install braintree
Enter fullscreen mode Exit fullscreen mode

Ruby:

gem install braintree
Enter fullscreen mode Exit fullscreen mode

Initialize the gateway:

const braintree = require('braintree')

const gateway = new braintree.BraintreeGateway({
  environment: braintree.Environment.Sandbox,
  merchantId: process.env.BRAINTREE_MERCHANT_ID,
  publicKey: process.env.BRAINTREE_PUBLIC_KEY,
  privateKey: process.env.BRAINTREE_PRIVATE_KEY
})
Enter fullscreen mode Exit fullscreen mode

Generate a client token

Before displaying the payment form, generate a client token to authorize frontend communication.

app.get('/checkout/token', async (req, res) => {
  const clientToken = await gateway.clientToken.generate()
  res.json({ clientToken: clientToken.clientToken })
})
Enter fullscreen mode Exit fullscreen mode

The frontend uses this token to initialize Drop-in UI or a custom integration.


Processing payments

The payment flow

  1. Frontend sends payment method nonce to your server
  2. Server creates a transaction using the nonce
  3. Braintree processes the payment
  4. Server receives success/failure response
  5. Fulfill the order or show an error

Charge a credit card

app.post('/checkout', async (req, res) => {
  const { paymentMethodNonce, amount, orderId } = req.body

  const result = await gateway.transaction.sale({
    amount: amount,
    paymentMethodNonce: paymentMethodNonce,
    orderId: orderId,
    options: {
      submitForSettlement: true
    }
  })

  if (result.success) {
    res.json({
      success: true,
      transactionId: result.transaction.id
    })
  } else {
    res.status(400).json({
      success: false,
      message: result.message
    })
  }
})
Enter fullscreen mode Exit fullscreen mode

Charge with saved payment method

After the first transaction, save the payment method for future use:

// Create customer with payment method
const result = await gateway.customer.create({
  firstName: 'John',
  lastName: 'Doe',
  email: 'john@example.com',
  paymentMethodNonce: nonce
})

// The payment method is saved
const paymentMethodToken = result.customer.paymentMethods[0].token

// Charge saved payment method later
await gateway.transaction.sale({
  amount: '49.99',
  paymentMethodToken: paymentMethodToken,
  options: {
    submitForSettlement: true
  }
})
Enter fullscreen mode Exit fullscreen mode

PayPal transactions

PayPal works the same as cards. The frontend gets a nonce from PayPal, then you charge it:

const result = await gateway.transaction.sale({
  amount: '99.00',
  paymentMethodNonce: paypalNonce,
  orderId: 'ORDER-123',
  options: {
    submitForSettlement: true
  }
})
Enter fullscreen mode Exit fullscreen mode

Refunds and voids

Full refund

const result = await gateway.transaction.refund('transaction_id')

if (result.success) {
  console.log('Refunded:', result.transaction.id)
}
Enter fullscreen mode Exit fullscreen mode

Partial refund

const result = await gateway.transaction.refund('transaction_id', '50.00')

if (result.success) {
  console.log('Partial refund processed')
}
Enter fullscreen mode Exit fullscreen mode

Void a transaction

Void stops a transaction before it settles (authorized but not captured):

const result = await gateway.transaction.void('transaction_id')

if (result.success) {
  console.log('Transaction voided')
}
Enter fullscreen mode Exit fullscreen mode

Transaction status flow

authorized → submitted_for_settlement → settled
                  ↓
                voided

settled → refunded
Enter fullscreen mode Exit fullscreen mode

Subscriptions and recurring billing

Braintree supports subscriptions for recurring payments.

Create a plan

Create a plan in the Braintree control panel or via API:

const result = await gateway.plan.create({
  id: 'monthly-premium',
  name: 'Monthly Premium',
  billingFrequency: 1,
  currencyIsoCode: 'USD',
  price: '29.99'
})
Enter fullscreen mode Exit fullscreen mode

Create a subscription

const result = await gateway.subscription.create({
  paymentMethodToken: paymentMethodToken,
  planId: 'monthly-premium',
  firstBillingDate: new Date()
})

if (result.success) {
  console.log('Subscription created:', result.subscription.id)
}
Enter fullscreen mode Exit fullscreen mode

Cancel a subscription

const result = await gateway.subscription.cancel('subscription_id')

if (result.success) {
  console.log('Subscription cancelled')
}
Enter fullscreen mode Exit fullscreen mode

Update subscription

const result = await gateway.subscription.update('subscription_id', {
  planId: 'annual-premium',
  price: '299.99'
})
Enter fullscreen mode Exit fullscreen mode

Webhooks for payment events

Webhooks notify your server about transaction events—critical for subscriptions and disputes.

Create a webhook endpoint

app.post('/webhooks/braintree', (req, res) => {
  const signature = req.body.bt_signature
  const payload = req.body.bt_payload

  // Verify and parse the webhook
  gateway.webhookNotification.parse(
    signature,
    payload,
    (err, webhookNotification) => {
      if (err) {
        return res.status(400).send('Invalid webhook')
      }

      switch (webhookNotification.kind) {
        case 'subscription_charged_successfully':
          handleSuccessfulCharge(webhookNotification.subscription)
          break
        case 'subscription_charged_unsuccessfully':
          handleFailedCharge(webhookNotification.subscription)
          break
        case 'dispute_opened':
          handleDispute(webhookNotification.dispute)
          break
        case 'transaction_settled':
          handleSettledTransaction(webhookNotification.transaction)
          break
      }

      res.status(200).send('OK')
    }
  )
})
Enter fullscreen mode Exit fullscreen mode

Register webhook in Braintree

In the Braintree control panel, navigate to Settings → Webhooks and add your endpoint URL. For local development, use a tunneling service like ngrok.


Testing with Apidog

Thoroughly test payment APIs—never use production data for integration tests. Apidog helps you test safely.

Apidog Screenshot

1. Mock webhook payloads

Instead of waiting for Braintree, create mock webhook payloads:

{
  "bt_signature": "test_signature",
  "bt_payload": "eyJraW5kIjoidHJhbnNhY3Rpb25fc2V0dGxlZCIsInRyYW5zYWN0aW9uIjp7ImlkIjoiYWJjMTIzIiwiYW1vdW50IjoiNDkuOTkiLCJzdGF0dXMiOiJzZXR0bGVkIn19"
}
Enter fullscreen mode Exit fullscreen mode

Send these to your webhook endpoint and verify your logic.

2. Environment separation

Maintain clear separation between sandbox and production:

# Sandbox
BRAINTREE_MERCHANT_ID: sandbox_merchant
BRAINTREE_PUBLIC_KEY: sandbox_public
BRAINTREE_PRIVATE_KEY: sandbox_private
BRAINTREE_ENVIRONMENT: sandbox

# Production
BRAINTREE_MERCHANT_ID: live_merchant
BRAINTREE_PUBLIC_KEY: live_public
BRAINTREE_PRIVATE_KEY: live_private
BRAINTREE_ENVIRONMENT: production
Enter fullscreen mode Exit fullscreen mode

3. Validate webhook responses

Example tests for webhook handling (e.g., in Postman):

pm.test('Webhook processed successfully', () => {
  pm.response.to.have.status(200)
  pm.response.to.have.body('OK')
})

pm.test('Transaction ID logged', () => {
  // Check your logs or database
  const transactionId = pm.environment.get('last_transaction_id')
  pm.expect(transactionId).to.not.be.empty
})
Enter fullscreen mode Exit fullscreen mode

Test Braintree webhooks with Apidog - free


Common errors and fixes

Processor Declined

Cause: The bank rejected the transaction.

Fix: Usually due to insufficient funds or fraud filters. Show a generic error and suggest another card. Log processorResponseCode for debugging.

if (!result.success) {
  if (result.transaction.processorResponseCode === '2000') {
    // Bank declined
    return res.status(400).json({
      error: 'Your bank declined this transaction. Please try a different card.'
    })
  }
}
Enter fullscreen mode Exit fullscreen mode

Gateway Rejected

Cause: Braintree’s fraud filters blocked the transaction.

Fix: Check gatewayRejectionReason:

if (result.transaction.gatewayRejectionReason === 'cvv') {
  // CVV mismatch
}
if (result.transaction.gatewayRejectionReason === 'avs') {
  // Address verification failed
}
if (result.transaction.gatewayRejectionReason === 'fraud') {
  // Advanced fraud tools blocked it
}
Enter fullscreen mode Exit fullscreen mode

Settlement failures

Cause: The transaction couldn’t settle after authorization.

Fix: Monitor transaction_settlement_declined webhooks. Common reasons:

  • Payment method expired between auth and settlement
  • Issuer blocked the transaction
  • Insufficient funds at settlement

Duplicate transactions

Cause: Customer double-clicked “Pay” or code retried.

Fix: Use the orderId parameter to prevent duplicates:

const result = await gateway.transaction.sale({
  amount: '49.99',
  paymentMethodNonce: nonce,
  orderId: 'UNIQUE-ORDER-123', // Prevents duplicates
  options: {
    submitForSettlement: true
  }
})
Enter fullscreen mode Exit fullscreen mode

Alternatives and comparisons

Feature Braintree Stripe PayPal
Pricing 2.9% + 30¢ 2.9% + 30¢ 2.9% + 30¢
PayPal support Native Add-on Native
Subscriptions Yes Yes Limited
International 46 countries 46 countries 200+ countries
Fraud tools Built-in Built-in Basic
SDK quality Excellent Excellent Good
Payouts Yes Yes Yes

Braintree’s main advantage is native PayPal and Venmo support. If you need both card processing and PayPal, Braintree can be simpler than using Stripe and PayPal separately.


Real-world use cases

SaaS subscription platform:

A project management tool uses Braintree for monthly subscriptions. Webhooks handle failed payments (e.g., card expired), triggering email notifications. Users update payment methods without support intervention.

Marketplace payments:

A freelance platform splits payments between platform and freelancer using Braintree’s merchant and sub-merchant setup.

E-commerce with PayPal:

An online store offers both credit cards and PayPal. Braintree’s unified API means a single integration covers both, with shared customer objects.


Conclusion

Key takeaways for implementing Braintree:

  • Braintree SDKs handle server-side payment processing
  • Client tokens authorize frontend communication
  • Transaction sales process credit cards and PayPal
  • Subscriptions manage recurring billing
  • Webhooks notify you about payment events
  • Always test thoroughly with Apidog before going live

FAQ

What’s a payment method nonce?

A nonce is a one-time token representing a payment method. The frontend generates it after a customer enters card details. Your server uses the nonce to charge the card. Nonces expire after 3 hours.

What’s the difference between authorization and settlement?

  • Authorization reserves funds on the card.
  • Settlement actually charges the card.

By default, Braintree auto-settles. For pre-orders, authorize first, then settle upon shipping:

// Authorize only
await gateway.transaction.sale({
  amount: '99.00',
  paymentMethodNonce: nonce,
  options: {
    submitForSettlement: false // Authorize only
  }
})

// Settle later
await gateway.transaction.submitForSettlement('transaction_id')
Enter fullscreen mode Exit fullscreen mode

How do I handle currency?

Each Braintree merchant account has a default currency. Multi-currency support requires multiple merchant accounts. Contact Braintree support for setup.

What test card numbers should I use?

Braintree provides sandbox test cards:

  • 4111111111111111 – Visa (success)
  • 4000111111111115 – Visa (decline)
  • 5555555555554444 – Mastercard (success)
  • 378282246310005 – Amex (success)

How do I handle disputes/chargebacks?

Listen for dispute_opened, dispute_won, and dispute_lost webhooks. Provide evidence in the Braintree control panel. Keep records—customer contacts, delivery proof, terms of service.

Can I store credit card numbers?

No. PCI compliance prohibits storing raw card numbers. Store payment method tokens only. Braintree manages PCI scope.

What’s 3D Secure?

3D Secure adds extra verification for card-not-present transactions. Enable in the control panel and handle authentication_required responses:

const result = await gateway.transaction.sale({
  amount: '100.00',
  paymentMethodNonce: nonce,
  threeDSecure: {
    required: true
  }
})
Enter fullscreen mode Exit fullscreen mode

How long do refunds take?

Refunds typically take 3-5 business days, depending on the customer’s bank. You’ll receive a transaction_refunded webhook when complete.

Top comments (0)