The nonce is the card
number you never see.
Braintree's Drop-in UI tokenizes card data inside a browser-hosted form. Your server never sees a raw card number — it receives a single-use nonce. Send that nonce to Braintree's SDK to charge the card. This mock gives you a complete nonce → sale → settlement flow without any Braintree credentials.
Client (browser)
Drop-in UI collects card
Tokenizes with Braintree
Your Server
Receives nonce (not card)
Calls gateway.transaction.sale()
Braintree API
Charges card, returns result
Raw card never leaves Braintree
Braintree is a PayPal-owned gateway that uses client-side tokenization. The Drop-in UI or Hosted Fields component runs in the customer's browser, collects card details, and returns a payment_method_nonce to your JavaScript. That nonce — not a card number — is what your server sends to Braintree. This keeps raw card data off your server entirely.
Settlement status is the most misunderstood part of Braintree. A transaction moves from authorized to submitted_for_settlement when you call sale() with submitForSettlement: true, then to settling once the bank batch runs, and finally to settled when funds land. processor_declined means the issuing bank said no. gateway_rejected means Braintree's own fraud filters blocked it before the bank even saw it.
Testing Braintree normally requires a sandbox merchant account with a merchant ID, public key, and private key — plus the Drop-in UI running on a real page. This mock gives you the full nonce-based transaction flow, all seven settlement statuses on demand, and webhook delivery to your endpoint without any of that setup.
What is a nonce and why does it matter?
A nonce (payment method nonce) replaces the raw card number in your server-side code. Instead of 4111 1111 1111 1111, your server gets something like tokencc_bj_mock_s2fk9k_.... Braintree knows which card it maps to. You don't.
This matters because PCI compliance scope is determined by where card data flows. If raw numbers hit your server, you need a full SAQ D audit. With tokenization, you drop down to SAQ A-EP — a much shorter checklist.
The Drop-in UI runs in an iframe Braintree controls. The user types into Braintree's input fields, not yours. When the form submits, Braintree's JavaScript calls home, gets a nonce, and passes it to your callback. Your JavaScript only ever sees the nonce.
SINGLE-USE TOKEN
A nonce is single-use and expires in 3 hours. Once your server passes it to gateway.transaction.sale(), it is consumed. If the sale call fails and the customer retries, the Drop-in UI generates a new nonce automatically.
Browser (Drop-in UI)
Sees raw card number — inside Braintree's iframe
Sends to Braintree → gets nonce back
Your server
Sees nonce + amount — never card number
Calls gateway.transaction.sale()
Braintree
Resolves nonce → card → charges bank
Returns transaction result
Settlement statuses
Braintree has more transaction states than most gateways. settled and submitted_for_settlement are not the same thing — and handling them differently matters.
Bank approved the authorization. Funds are reserved but not yet captured. You can either submit for settlement or void.
The transaction has been queued to the card network for settlement. Typically happens at end of day.
Settlement is in progress. The card network is transferring funds. Usually lasts a few hours.
Funds have landed in your Braintree merchant account. Final confirmed state — 1 to 3 days after submission.
Transaction was voided before settlement. No funds were moved. Only possible before submitted_for_settlement.
The issuing bank said no. Check processorResponseCode for the reason (insufficient funds, stolen card, etc.).
Braintree's own fraud filter blocked the transaction before it reached the bank. CVV mismatch, AVS failure, or duplicate.
Integration code
// Server: generate a client token and send it to the browser
// Node.js example
const gateway = new braintree.BraintreeGateway({
environment: braintree.Environment.Sandbox,
merchantId: 'your_merchant_id',
publicKey: 'your_public_key',
privateKey: 'your_private_key',
});
app.get('/client-token', async (req, res) => {
const response = await gateway.clientToken.generate({});
res.json({ clientToken: response.clientToken });
});
// MockGateway endpoint (no real credentials needed):
// GET https://mockgateway.dev/api/base/braintree/client-token
// Response: { "clientToken": "mock_ct_eyJhbGciOi..." }New to testing webhooks locally? Read how webhook delivery and retries work →
Request parameters
Sent to POST /api/base/braintree/init
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | required | The mutation or query to run. chargePaymentMethod creates a transaction; a node query reads one back. e.g. mutation ChargePaymentMethod($input: ChargePaymentMethodInput!) { chargePaymentMethod(input: $input) { transaction { id status } } } |
variables | object | required | Values for the query. The transaction amount travels at variables.input.transaction.amount. e.g. {"input": {"paymentMethodId": "tokencc_xxx", "transaction": {"amount": "10.00"}}} |
Response fields
| Field | Type | Description |
|---|---|---|
id | string | Braintree transaction ID |
status | enum | Transaction status |
type | string | Transaction type |
created_at | datetime | Creation timestamp |
Questions about Braintree
Other gateway templates
Looking for a different provider?