Integrating Braintree with Lovable uses Edge Functions to generate client tokens, then Braintree's Drop-in UI handles payment collection and returns a payment method nonce to the browser. Your Edge Function sends that nonce to Braintree to complete the charge. Store your Merchant ID, Public Key, and Private Key in Cloud Secrets. Setup takes 30 minutes.
Why integrate Braintree with Lovable?
Braintree, owned by PayPal since 2013, is one of the few payment gateways that natively supports PayPal, Venmo, Apple Pay, Google Pay, and credit cards all through a single Drop-in UI without separate integrations for each. For consumer-facing apps where a significant portion of users already have PayPal or Venmo accounts, Braintree dramatically reduces checkout friction — users can complete purchases without entering card details at all. This is particularly valuable for US-based apps targeting millennials and Gen Z who are heavy Venmo users.
For Lovable developers, Braintree is the right choice when PayPal acceptance is a business requirement (e.g., an eBay-integrated marketplace, a consumer app where PayPal is a primary payment method), when you need Venmo checkout (unique to Braintree among major gateways), or when you want a single gateway that handles international card payments alongside US-specific methods. Braintree also offers a mature recurring billing API called Braintree Subscriptions, which handles plan management, prorations, and failed payment retries without requiring custom scheduler logic.
The integration pattern follows a client-token flow: your Edge Function calls Braintree to generate a client token, the browser uses that token to initialize the Drop-in UI, the user selects a payment method and authorizes it, and the Drop-in returns a payment method nonce. Your second Edge Function receives this nonce and calls Braintree to create a transaction. Braintree's server-side SDK (braintree-node on npm) simplifies this, though in Deno Edge Functions you'll use the Braintree REST API directly.
Integration method
Braintree has no native Lovable connector. Integration uses Supabase Edge Functions to generate client tokens (which initialize the Drop-in UI) and to process payment nonces returned by the Drop-in into completed transactions. Braintree's Merchant ID, Public Key, and Private Key are stored in Cloud Secrets. The Drop-in handles all payment method selection including PayPal, Venmo, and cards.
Prerequisites
- A Lovable project with Cloud enabled
- A Braintree sandbox account — create free at sandbox.braintreegateway.com
- Your Braintree Merchant ID, Public Key, and Private Key from the Braintree Control Panel under Account → My User → API Keys
- For PayPal: a PayPal sandbox account linked to your Braintree sandbox
- Basic understanding of the payment nonce concept (a single-use token representing a payment method)
Step-by-step guide
Get Braintree API credentials and add to Cloud Secrets
Get Braintree API credentials and add to Cloud Secrets
Log into the Braintree sandbox Control Panel at sandbox.braintreegateway.com. Navigate to Account → My User → API Keys, Tokenization Keys, Encryption Keys. Under 'API Keys', you'll see your existing keys or can generate a new one. Each key has three components: Merchant ID, Public Key, and Private Key. The Merchant ID identifies your account and is safe to expose if needed (though we keep it server-side as good practice). The Public Key is used as the username in Basic Auth. The Private Key is the password equivalent — keep it secret. Never use the tokenization key for server-side calls; that's a different, browser-safe key for tokenization-only operations. For PayPal: in your Braintree sandbox, go to Settings → Processing → Payment Methods and ensure PayPal is enabled. Link your PayPal sandbox account if prompted. This allows the Braintree Drop-in to show the PayPal button. In your Lovable project, open Cloud tab → Secrets and add: BRAINTREE_MERCHANT_ID (your merchant ID), BRAINTREE_PUBLIC_KEY (your public key), BRAINTREE_PRIVATE_KEY (your private key), and BRAINTREE_ENVIRONMENT set to 'sandbox' or 'production'.
Pro tip: The Braintree Merchant ID, Public Key, and Private Key are all visible on the same 'API Keys' page. Don't confuse the 'Public Key' (used server-side with Private Key) with a 'Tokenization Key' (browser-only, different use case).
Expected result: BRAINTREE_MERCHANT_ID, BRAINTREE_PUBLIC_KEY, BRAINTREE_PRIVATE_KEY, and BRAINTREE_ENVIRONMENT stored in Cloud Secrets.
Create an Edge Function to generate Braintree client tokens
Create an Edge Function to generate Braintree client tokens
The Braintree Drop-in UI requires a client token to initialize. This token encodes your merchant account information and creates a secure channel between the browser and Braintree. Your Edge Function generates it by calling the Braintree API — the client token is then safe to pass to the browser since it's a temporary, limited-permission token. Create supabase/functions/braintree-client-token/index.ts. The Braintree REST API endpoint for generating client tokens is POST https://payments.sandbox.braintree-api.com/graphql (Braintree uses GraphQL for their newer API) or the legacy REST endpoint https://api.sandbox.braintreegateway.com/merchants/{merchantId}/client_token. Use HTTP Basic Auth with your Public Key as username and Private Key as password. For the legacy REST approach (simpler for Deno without the braintree SDK), call POST https://api.sandbox.braintreegateway.com/merchants/{MERCHANT_ID}/client_token with Basic Auth and an empty JSON body {}. Set the Accept header to 'application/json' and Content-Type to 'application/json'. The response is JSON with a clientToken field (a base64-encoded string). Return this token to your frontend. If the request is for a returning customer, pass { customerId: '...' } in the body to get a token scoped to that customer's saved payment methods.
Create a Supabase Edge Function at supabase/functions/braintree-client-token/index.ts. Accept GET or POST requests. Call the Braintree sandbox API at https://api.sandbox.braintreegateway.com/merchants/{BRAINTREE_MERCHANT_ID}/client_token using Basic Auth with BRAINTREE_PUBLIC_KEY as username and BRAINTREE_PRIVATE_KEY as password from Deno.env.get(). If the request body contains a customerId, include it in the Braintree API request body to get a customer-scoped token. Return the clientToken from the response. Include CORS headers.
Paste this in Lovable chat
1import { serve } from "https://deno.land/std@0.168.0/http/server.ts";23const MERCHANT_ID = Deno.env.get("BRAINTREE_MERCHANT_ID") ?? "";4const PUBLIC_KEY = Deno.env.get("BRAINTREE_PUBLIC_KEY") ?? "";5const PRIVATE_KEY = Deno.env.get("BRAINTREE_PRIVATE_KEY") ?? "";6const ENVIRONMENT = Deno.env.get("BRAINTREE_ENVIRONMENT") ?? "sandbox";78const BASE_URL = ENVIRONMENT === "production"9 ? `https://api.braintreegateway.com/merchants/${MERCHANT_ID}`10 : `https://api.sandbox.braintreegateway.com/merchants/${MERCHANT_ID}`;1112const corsHeaders = {13 "Access-Control-Allow-Origin": "*",14 "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",15};1617serve(async (req) => {18 if (req.method === "OPTIONS") {19 return new Response("ok", { headers: corsHeaders });20 }2122 try {23 let customerId: string | undefined;24 if (req.method === "POST") {25 const body = await req.json().catch(() => ({}));26 customerId = body.customerId;27 }2829 const authString = btoa(`${PUBLIC_KEY}:${PRIVATE_KEY}`);30 const res = await fetch(`${BASE_URL}/client_token`, {31 method: "POST",32 headers: {33 "Authorization": `Basic ${authString}`,34 "Content-Type": "application/json",35 "Accept": "application/json",36 "Braintree-Version": "2019-01-01",37 },38 body: JSON.stringify(customerId ? { customerId } : {}),39 });4041 if (!res.ok) {42 const err = await res.text();43 throw new Error(`Braintree error: ${res.status} ${err}`);44 }4546 const data = await res.json();4748 return new Response(49 JSON.stringify({ clientToken: data.clientToken }),50 { headers: { ...corsHeaders, "Content-Type": "application/json" } }51 );52 } catch (e) {53 return new Response(54 JSON.stringify({ error: (e as Error).message }),55 { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } }56 );57 }58});Pro tip: Client tokens are valid for 24 hours but should be treated as single-use per checkout session. Generate a new token for each checkout attempt rather than caching them across sessions.
Expected result: Edge Function returning a base64 clientToken string when called. The token is ready to be passed to the Braintree Drop-in UI.
Embed the Braintree Drop-in UI in React
Embed the Braintree Drop-in UI in React
Braintree's Drop-in UI is a pre-built payment form that handles card collection, PayPal redirects, Venmo, Apple Pay, and Google Pay in a single component. Add the Drop-in library to your project by including the CDN script in index.html: <script src='https://js.braintreegateway.com/web/dropin/1.43.0/js/dropin.min.js'></script>. In your React component, call your braintree-client-token Edge Function on mount to get a clientToken. Then call braintree.dropin.create() with the clientToken and a selector for your container div. This initializes the Drop-in, which renders the payment form inside that div. Store the Drop-in instance in a ref so you can call requestPaymentMethod() on it when the user submits. When the user clicks your Pay button, call dropinInstance.requestPaymentMethod(). If the user hasn't entered valid payment details, the Drop-in shows validation errors automatically. If successful, requestPaymentMethod() returns an object with a nonce string — this is the single-use payment method nonce you send to your Edge Function to complete the transaction. The nonce represents the selected payment method (card, PayPal, or Venmo) without exposing any sensitive data. Configure PayPal in the dropin.create() options by including the paypal property with flow 'checkout' and your transaction amount. This shows the PayPal button inside the Drop-in automatically when your Braintree account has PayPal enabled.
Add the Braintree Drop-in UI to the checkout page. First add the Braintree Drop-in JS CDN to index.html head. Create a BraintreeCheckout component that: 1) fetches a clientToken from the braintree-client-token Edge Function on mount, 2) calls window.braintree.dropin.create with the clientToken, a '#dropin-container' div selector, and paypal: { flow: 'checkout', amount: totalAmount, currency: 'USD' } for PayPal support, 3) stores the dropin instance in a ref, 4) when Pay button is clicked, calls dropinInstance.requestPaymentMethod() to get the nonce, 5) sends the nonce and amount to a braintree-process-payment Edge Function, 6) shows success or error state based on the response.
Paste this in Lovable chat
1import { useEffect, useRef, useState } from "react";2import { supabase } from "@/integrations/supabase/client";3import { Button } from "@/components/ui/button";4import { toast } from "@/hooks/use-toast";56declare global {7 interface Window {8 braintree: {9 dropin: {10 create: (options: Record<string, unknown>) => Promise<BraintreeDropinInstance>;11 };12 };13 }14}1516interface BraintreeDropinInstance {17 requestPaymentMethod: () => Promise<{ nonce: string; type: string }>;18 teardown: () => Promise<void>;19}2021interface BraintreeCheckoutProps {22 amount: number;23 orderId: string;24 onSuccess: (transactionId: string) => void;25}2627export function BraintreeCheckout({ amount, orderId, onSuccess }: BraintreeCheckoutProps) {28 const dropinRef = useRef<BraintreeDropinInstance | null>(null);29 const [loading, setLoading] = useState(false);30 const [ready, setReady] = useState(false);3132 useEffect(() => {33 async function initDropin() {34 const { data, error } = await supabase.functions.invoke("braintree-client-token", { body: {} });35 if (error || !data?.clientToken) {36 toast({ title: "Checkout error", description: "Could not initialize payment form", variant: "destructive" });37 return;38 }39 dropinRef.current = await window.braintree.dropin.create({40 authorization: data.clientToken,41 container: "#dropin-container",42 paypal: { flow: "checkout", amount: amount.toFixed(2), currency: "USD" },43 venmo: { allowNewBrowserTab: false },44 });45 setReady(true);46 }47 initDropin();48 return () => { dropinRef.current?.teardown(); };49 }, [amount]);5051 const handlePay = async () => {52 if (!dropinRef.current) return;53 setLoading(true);54 try {55 const { nonce, type } = await dropinRef.current.requestPaymentMethod();56 const { data, error } = await supabase.functions.invoke("braintree-process-payment", {57 body: { nonce, amount, orderId, paymentType: type },58 });59 if (error || !data?.success) {60 toast({ title: "Payment failed", description: data?.error ?? "Unknown error", variant: "destructive" });61 } else {62 onSuccess(data.transactionId);63 }64 } catch (e) {65 toast({ title: "Payment error", description: (e as Error).message, variant: "destructive" });66 } finally {67 setLoading(false);68 }69 };7071 return (72 <div className="max-w-md mx-auto p-6 bg-white rounded-xl shadow space-y-4">73 <h2 className="text-xl font-semibold">Complete Purchase — ${amount.toFixed(2)}</h2>74 <div id="dropin-container" />75 <Button onClick={handlePay} disabled={!ready || loading} className="w-full">76 {loading ? "Processing..." : "Pay Now"}77 </Button>78 </div>79 );80}Pro tip: Venmo requires the user to be on a mobile device with the Venmo app installed. On desktop browsers, the Venmo option is hidden automatically by the Drop-in. You don't need to implement device detection yourself.
Expected result: Braintree Drop-in renders with card fields and PayPal button visible. Selecting PayPal opens the PayPal sandbox authentication popup.
Create an Edge Function to process payment nonces
Create an Edge Function to process payment nonces
The final Edge Function receives the payment method nonce from the frontend and creates a Braintree transaction. The Braintree Transaction Sale API endpoint is POST https://api.sandbox.braintreegateway.com/merchants/{merchantId}/transactions using Basic Auth with your Public Key and Private Key. The request body is JSON with amount (as a string with two decimal places, e.g., '19.99'), paymentMethodNonce (the nonce from the Drop-in), and options including submitForSettlement set to true (immediate capture, not just authorization). You can also pass orderId to link the Braintree transaction to your internal order ID, and customer information for better fraud scoring. Braintree returns a transaction object with a status field. 'submitted_for_settlement' means the payment was authorized and will settle within 24 hours. 'processor_declined' means the card was declined — read processorResponseText for the reason. 'gateway_rejected' means Braintree's fraud detection blocked it — check gatewayRejectionReason. Always store the transaction id (Braintree's 8-character alphanumeric ID) in Supabase alongside your order for refund capability.
Create a Supabase Edge Function at supabase/functions/braintree-process-payment/index.ts. Accept POST requests with nonce, amount (number), orderId, and paymentType. Call the Braintree sandbox transactions API at https://api.sandbox.braintreegateway.com/merchants/{BRAINTREE_MERCHANT_ID}/transactions using Basic Auth. Set amount as a 2-decimal string, paymentMethodNonce to the nonce, and options.submitForSettlement to true. If transaction status is 'submitted_for_settlement' or 'authorized', return success with the transaction id. Otherwise return the processor error. Update the order in Supabase on success using service role key.
Paste this in Lovable chat
1import { serve } from "https://deno.land/std@0.168.0/http/server.ts";2import { createClient } from "https://esm.sh/@supabase/supabase-js@2";34const MERCHANT_ID = Deno.env.get("BRAINTREE_MERCHANT_ID") ?? "";5const PUBLIC_KEY = Deno.env.get("BRAINTREE_PUBLIC_KEY") ?? "";6const PRIVATE_KEY = Deno.env.get("BRAINTREE_PRIVATE_KEY") ?? "";7const ENVIRONMENT = Deno.env.get("BRAINTREE_ENVIRONMENT") ?? "sandbox";8const SUPABASE_URL = Deno.env.get("SUPABASE_URL") ?? "";9const SUPABASE_SERVICE_ROLE_KEY = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? "";1011const BASE_URL = ENVIRONMENT === "production"12 ? `https://api.braintreegateway.com/merchants/${MERCHANT_ID}`13 : `https://api.sandbox.braintreegateway.com/merchants/${MERCHANT_ID}`;1415const corsHeaders = {16 "Access-Control-Allow-Origin": "*",17 "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",18};1920serve(async (req) => {21 if (req.method === "OPTIONS") {22 return new Response("ok", { headers: corsHeaders });23 }2425 try {26 const { nonce, amount, orderId, paymentType } = await req.json();27 const authString = btoa(`${PUBLIC_KEY}:${PRIVATE_KEY}`);2829 const payload = {30 transaction: {31 type: "sale",32 amount: Number(amount).toFixed(2),33 paymentMethodNonce: nonce,34 orderId,35 options: { submitForSettlement: true },36 channel: "Lovable",37 },38 };3940 const res = await fetch(`${BASE_URL}/transactions`, {41 method: "POST",42 headers: {43 "Authorization": `Basic ${authString}`,44 "Content-Type": "application/json",45 "Accept": "application/json",46 "Braintree-Version": "2019-01-01",47 },48 body: JSON.stringify(payload),49 });5051 const data = await res.json();52 const tx = data.transaction ?? data;53 const successStatuses = ["submitted_for_settlement", "authorized", "settling", "settled"];5455 if (successStatuses.includes(tx.status)) {56 const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY);57 await supabase.from("orders").update({58 status: "paid",59 braintree_transaction_id: tx.id,60 payment_method: paymentType,61 }).eq("id", orderId);6263 return new Response(64 JSON.stringify({ success: true, transactionId: tx.id }),65 { headers: { ...corsHeaders, "Content-Type": "application/json" } }66 );67 } else {68 const errorMsg = tx.processorResponseText ?? tx.gatewayRejectionReason ?? tx.status ?? "Transaction failed";69 return new Response(70 JSON.stringify({ success: false, error: errorMsg }),71 { status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } }72 );73 }74 } catch (e) {75 return new Response(76 JSON.stringify({ error: (e as Error).message }),77 { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } }78 );79 }80});Pro tip: Use Braintree test nonces for unit testing without a real Drop-in: the nonce 'fake-valid-nonce' always succeeds, and 'fake-processor-declined-visa-nonce' always declines. These bypass the Drop-in UI for automated testing.
Expected result: Complete payment flow working: client token fetched, Drop-in rendered, payment method selected, nonce processed, transaction appears in Braintree sandbox Control Panel, order updated in Supabase.
Common use cases
Consumer checkout with PayPal and card options
A consumer marketplace for handmade goods offers both PayPal and credit card checkout through Braintree's Drop-in. Buyers who already have PayPal can complete the purchase in two clicks without entering a card. First-time buyers can enter card details. The Braintree Drop-in shows both options automatically when configured with PayPal enabled. A confirmation page shows the transaction ID and estimated fulfillment time.
Add a Braintree checkout to the product page. Call an Edge Function to get a Braintree client token on page load. Initialize the Braintree Drop-in UI with the client token and paypal: { flow: 'checkout', amount: productPrice, currency: 'USD' } to enable PayPal alongside cards. When the user clicks Buy, call requestPaymentMethod() on the Drop-in instance to get a nonce, then send it to a process-payment Edge Function. On success, create an order in Supabase with status 'paid', Braintree transaction ID, and payment method type (card or PayPal).
Copy this prompt to try it in Lovable
Venmo checkout for US mobile users
A peer-to-peer services app (task completion, tutoring, pet care) wants to enable Venmo checkout for US mobile users. The Braintree Drop-in shows the Venmo option automatically on mobile browsers. Users who have Venmo installed are redirected to the Venmo app to authorize the payment, then returned to the service app. Braintree captures the transaction and the Edge Function records payment confirmation in Supabase.
Enable Venmo payments in the checkout flow using Braintree. When initializing the Braintree Drop-in, set venmo: { allowNewBrowserTab: false } to keep users in the same browser tab on mobile. Detect mobile devices and show a 'Pay with Venmo' button prominently. When the user taps it, the Drop-in handles the Venmo app redirect flow. After authorization, send the nonce to the process-payment Edge Function. Store the Venmo username from the transaction details in the Supabase payments table for receipt reference.
Copy this prompt to try it in Lovable
Subscription membership with Braintree recurring billing
A professional membership site uses Braintree Subscriptions to manage monthly and annual plans. During signup, the Edge Function creates a Braintree customer with the payment method nonce, then subscribes them to a Braintree subscription plan. Braintree handles all recurring billing, sends webhook events for successful charges and payment failures, and manages dunning (retry logic) automatically.
Build subscription management using Braintree. During signup, call an Edge Function that creates a Braintree Customer with the payment nonce (using the Braintree Customers API), then creates a Subscription on plan ID 'monthly_basic' or 'annual_pro' depending on what the user selected. Store the Braintree subscriptionId and customerId in Supabase. Set up a webhook Edge Function to receive subscription_charged_successfully and subscription_went_past_due events. On successful charge, extend the user's access date. On past due, send a dunning email.
Copy this prompt to try it in Lovable
Troubleshooting
Drop-in shows 'An unknown error occurred' or fails to render
Cause: The client token is malformed, expired, or the Braintree Drop-in JS library version in the CDN doesn't support the token format returned by your Edge Function.
Solution: Check that the clientToken returned by your Edge Function is a non-empty string. Log it in browser console and verify it's a base64-encoded string (should start with 'eyJ'). Make sure the Braintree Drop-in CDN script is loaded before your component runs — add it to index.html head, not deferred. Try regenerating a fresh client token.
1// Test your client token endpoint directly:2const { data } = await supabase.functions.invoke('braintree-client-token', { body: {} });3console.log('Token prefix:', data?.clientToken?.substring(0, 20));Transaction returns 'processor_declined' with processorResponseCode '2001'
Cause: The sandbox card used for testing was declined. In Braintree sandbox, specific amounts and card numbers trigger specific outcomes. Amount $2001.00 triggers a decline in sandbox.
Solution: Use Braintree's fake-valid-nonce for testing successful transactions in sandbox without a real Drop-in. For testing declines, use the nonce 'fake-processor-declined-visa-nonce'. Avoid using amounts like $2001.00 in sandbox testing. In production, 2001 means 'Do Not Honor' from the issuing bank — show the customer a message to contact their bank or use a different card.
PayPal button shows in the Drop-in but clicking it does nothing or shows 'Popup blocked'
Cause: The PayPal popup was blocked by the browser's popup blocker because it wasn't triggered by a direct user click, or PayPal is not properly configured in the Braintree sandbox account.
Solution: Ensure the PayPal popup is initiated from a direct click handler, not from within an async function that runs after a delay. In Braintree sandbox, navigate to Settings → Processing and confirm PayPal is enabled. For PayPal sandbox testing, link a PayPal sandbox buyer account at developer.paypal.com.
Edge Function returns 401 for client token or transaction API
Cause: The BRAINTREE_PUBLIC_KEY and BRAINTREE_PRIVATE_KEY combination is wrong, or you're mixing sandbox and production credentials with wrong API endpoints.
Solution: Verify in your Braintree Control Panel that BRAINTREE_ENVIRONMENT in Cloud Secrets matches your credentials (sandbox credentials only work against sandbox endpoints). Regenerate your API keys in the Control Panel under Account → My User → API Keys if needed, then update Cloud Secrets with the new values.
Best practices
- Store BRAINTREE_MERCHANT_ID, BRAINTREE_PUBLIC_KEY, and BRAINTREE_PRIVATE_KEY in Cloud Secrets — the Public Key is called 'public' relative to the Private Key, not relative to the browser, and should never be in frontend code
- Always use submitForSettlement: true in your transaction request unless you specifically need a two-step auth-then-capture flow — most apps should capture immediately
- Store the Braintree transaction ID (8-character string like 'abc123de') in Supabase for every successful charge — this is required to issue refunds, respond to chargebacks, and correlate payments with customer records
- Use Braintree's fake nonces (fake-valid-nonce, fake-processor-declined-visa-nonce) in Edge Function unit tests so you can test payment logic without a real Drop-in or real cards
- Generate a new client token for each checkout session — don't cache tokens across users or page reloads, as a token associated with Customer A's saved payment methods must never be shown to Customer B
- Enable Venmo only for production if your user base is US-based — Venmo requires users to have the Venmo app and a US bank account, so enabling it globally adds confusion for non-US users
- For subscription billing using Braintree Plans and Subscriptions, test the full lifecycle in sandbox including failed payment retries and subscription cancellation before going live
Alternatives
Stripe has better documentation and is easier to set up for most use cases; Braintree is preferred when native PayPal and Venmo checkout are required without separate integrations.
Adyen offers broader international payment methods and enterprise POS features; Braintree is simpler for US consumer apps where PayPal ecosystem coverage is the primary requirement.
PayPal Payouts focuses on disbursing money to recipients rather than accepting payments; combining Braintree for acceptance with PayPal Payouts for disbursement covers the full money-in-money-out marketplace lifecycle.
Frequently asked questions
Is Braintree owned by PayPal? Are they the same product?
Braintree was acquired by PayPal in 2013 for $800 million. They remain separate products with different APIs, control panels, and use cases. Braintree is the developer-focused payment gateway with support for PayPal, Venmo, and cards through a unified API. PayPal is the consumer-facing payment network. When you use Braintree, you can offer PayPal as a payment option to your customers without needing a separate PayPal Business integration.
Does Braintree support PayPal's Buy Now Pay Later (BNPL) features?
Yes — Braintree supports PayPal Pay Later (formerly Pay in 4 and PayPal Credit) through the Drop-in UI. When you configure the paypal: { flow: 'checkout' } option in the Drop-in, PayPal's BNPL options appear automatically for eligible US customers. No additional configuration is required. BNPL availability depends on the customer's PayPal account eligibility, not your merchant configuration.
What test cards and nonces work in Braintree sandbox?
For the Drop-in UI: use card 4111 1111 1111 1111 (Visa, any future date, any CVV) for a successful transaction. For testing without the Drop-in, use the nonce 'fake-valid-nonce' for success or 'fake-processor-declined-visa-nonce' for a decline. For PayPal sandbox, use a PayPal sandbox buyer account created at developer.paypal.com. Full test card and nonce documentation is at developer.paypal.com/braintree/docs/guides/3d-secure/testing-go-live.
How do I issue refunds through Braintree?
Call the Braintree Transactions API with PUT https://api.sandbox.braintreegateway.com/merchants/{merchantId}/transactions/{transactionId}/refund. Include the refund amount (or omit for full refund). Transactions can only be refunded after settlement (next day for most payments). For same-day voids of unsettled transactions, use the void endpoint instead. Store the original Braintree transaction ID in Supabase when processing payments to enable programmatic refunds.
Can Braintree handle non-USD currencies?
Yes — Braintree supports multiple currencies depending on your merchant account's configuration. You can specify the currency in your transaction request. However, your Braintree merchant account must be configured to accept the specific currency, and currency support varies by merchant country. US Braintree accounts primarily support USD. European Braintree accounts support EUR and GBP. Contact Braintree support to enable additional currencies for your specific merchant account.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation