Documentation Index
Fetch the complete documentation index at: https://docs.paypal.ai/llms.txt
Use this file to discover all available pages before exploring further.
With PayPal’s JavaScript SDK v6, you can support Strong Customer Authentication (SCA) and 3D Secure (3DS) in two payment flows:
- One-time payments process a single transaction with 3DS when required. The SDK collects card details, creates an order, and captures payment upon approval.
- Save payment methods (vaulting) store cards for future purchases. The server creates setup tokens, and the client submits card details to complete vaulting with optional 3DS.
One-time payment
On your server, create an order and define SCA and 3DS settings. Use payment_source.card.attributes.verification.method and supply return and cancel URLs in experience_context.
// POST /paypal-api/checkout/orders
// Body (example — adapt amounts, currency, and URLs)
{
"intent": "CAPTURE",
"purchase_units": [
{ "amount": { "currency_code": "USD", "value": "100.00" } }
],
"payment_source": {
"card": {
"attributes": {
"verification": { "method": "SCA_ALWAYS" } // or SCA_WHEN_REQUIRED
},
"experience_context": {
"return_url": "https://example.com/returnUrl",
"cancel_url": "https://example.com/cancelUrl"
}
}
}
}
Verification methods
- Use
SCA_ALWAYS to attempt 3DS for eligible cards and regions.
- Use
SCA_WHEN_REQUIRED to rely on network or regulatory mandates.
Submit card details
On the client side, initialize the SDK and card fields. Submit the order and handle each possible outcome.
const sdk = await window.paypal.createInstance({ clientId: "YOUR_CLIENT_ID", components: ["card-fields"] });
const session = sdk.createCardFieldsOneTimePaymentSession();
// Render fields
const numberField = session.createCardFieldsComponent({ type: "number", placeholder: "Card number" });
const cvvField = session.createCardFieldsComponent({ type: "cvv", placeholder: "CVV" });
const expiryField = session.createCardFieldsComponent({ type: "expiry", placeholder: "MM/YY" });
["#paypal-card-fields-number", "#paypal-card-fields-cvv", "#paypal-card-fields-expiry"]
.map((sel, i) => document.querySelector(sel).appendChild([numberField, cvvField, expiryField][i]));
// Pay click
document.querySelector("#pay-button").addEventListener("click", async () => {
try {
const orderId = await createOrder({ enable3DS: true, scaMethod: getScaMethodFromUI() });
const { state, data } = await session.submit(orderId, {
billingAddress: { postalCode: "95131" }
});
switch (state) {
case "succeeded": {
const { orderId, liabilityShift } = data
// 3DS may or may not have occurred; Use liabilityShift
// to determine if the payment should be captured
const result = await captureOrder(data.orderId);
// success UI
break;
}
case "canceled": {
// buyer closed 3DS modal
break;
}
case "failed": {
// inspect data.message
break;
}
}
} catch (e) { console.error(e); }
});
Example order endpoint
Use this Express handler to create a PayPal order and optionally include 3DS parameters based on your request body.
app.post("/paypal-api/checkout/orders", async (req, res) => {
const { enable3DS, scaMethod = "SCA_WHEN_REQUIRED" } = req.body || {};
const body = {
intent: "CAPTURE",
purchase_units: [{ amount: { currency_code: "USD", value: "100.00" } }],
};
if (enable3DS) {
body.payment_source = {
card: {
attributes: { verification: { method: scaMethod } },
experience_context: {
return_url: "https://example.com/returnUrl",
cancel_url: "https://example.com/cancelUrl",
},
},
};
}
// call PayPal Orders API with body and return { id }
});
Save payment method
On the server, create a vault setup token to save a payment method and require 3DS. Specify a verification method in the request.
// POST /paypal-api/vault/setup-token
// Body (example)
{
"payment_source": {
"card": {
"experience_context": {
"return_url": "https://example.com/returnUrl",
"cancel_url": "https://example.com/cancelUrl"
},
"verification_method": "SCA_ALWAYS" // or SCA_WHEN_REQUIRED
}
}
}
Submit card with setup token
On the client side, initialize the SDK and card fields. Submit the order and handle each possible outcome.
const sdk = await window.paypal.createInstance({ clientId: "YOUR_CLIENT_ID", components: ["card-fields"] });
const session = sdk.createCardFieldsSavePaymentSession();
// Render 3 fields as shown above...
const { setupToken } = await createVaultSetupToken({ enable3DS: true, scaMethod: getScaMethodFromUI() });
const { state, data } = await session.submit(setupToken);
switch (state) {
case "succeeded": {
// Persist data.vaultSetupToken on your server (maps to a vaulted instrument)
break;
}
case "canceled": {
// user dismissed 3DS
break;
}
case "failed": {
// show retry message
break;
}
}
Best practices
- Include both the
return_url and cancel_url in all 3DS requests.
- Handle every submit state and add
break; in each switch case.
- Track the liability shift in
data to manage risk.