PayPal Vaulting: One-Click Repeat Purchases

Save a customer’s PayPal account on their first purchase and charge it with a single click on every purchase after β€” no PayPal login or approval popup.

Overview

Every standard PayPal checkout asks the customer to approve the payment inside PayPal β€” a popup, a login, and an approval click before the purchase completes. That context switch is where repeat buyers drop off: they have already trusted you with a purchase, but every subsequent checkout makes them prove it again.

PayPal vaulting removes that step for returning customers. When vaulting is enabled on your account, PayPal securely saves (β€œvaults”) the customer’s PayPal account after their first successful payment. From then on, checkouts against that saved account complete in a single click β€” Coinflow charges the vaulted account directly and the payment is authorized immediately, with no PayPal popup, no login, and no approval screen.

The result is a materially lower-friction checkout that converts better where it matters most:

  • One click instead of three-plus steps. The customer never leaves your checkout β€” no popup to allow, no PayPal credentials to re-enter, no approval screen.
  • Higher repeat-purchase conversion. Each removed step is an exit ramp; vaulting removes all of them for the buyers most likely to purchase again.
  • Mobile-friendly by default. Popup and redirect approval flows are most fragile on mobile browsers; vaulted checkouts skip them entirely.
  • Instant feedback. The payment is authorized synchronously, so you can show a confirmation the moment the request returns instead of waiting on an approval round-trip.

Vaulting is a per-account setting β€” contact your Coinflow integrations team to enable it. If you use Coinflow’s prebuilt checkout UI or the CoinflowPayPalButton component, the first-purchase save then happens automatically. Direct API integrations opt in per order with the vault flag described below.

How It Works

  1. Vaulting is enabled on your Coinflow account by the integrations team.
  2. A customer completes a normal PayPal checkout (new or saved) with vault: true and approves the payment in PayPal as usual β€” PayPal’s approval screen discloses that the account will be saved.
  3. As part of that successful payment, PayPal vaults the customer’s account and Coinflow stores the resulting vault token against the customer’s saved PayPal account automatically.
  4. On later checkouts, the customer’s saved PayPal account carries vaulted: true β€” your signal that the one-click path is available.
  5. You call the PayPal Vaulted Checkout endpoint with the saved account’s token and a FraudNet clientMetadataId (see Collecting Fraud Data with FraudNet). Coinflow charges the vaulted account and authorizes the payment immediately β€” there is no approval step for the customer.
  6. Your backend receives the Authorized webhook right away, followed by Settled when funds are delivered, exactly as with any other PayPal payment.

Requesting Vaulting on a Purchase

Pass vault: true on the new or saved checkout to have PayPal save the account when the payment succeeds. Vaulting orders require both returnUrl and cancelUrl β€” PayPal rejects vaulting orders without redirect URLs, so the request is rejected with a 400 when they are missing.

$curl --location 'https://api-sandbox.coinflow.cash/api/checkout/paypal/<YOUR_MERCHANT_ID>' \
>--header 'accept: application/json' \
>--header 'content-type: application/json' \
>--header 'x-coinflow-auth-session-key: <YOUR_SESSION_KEY>' \
>--data-raw '{
> "subtotal": {
> "cents": 500,
> "currency": "USD"
> },
> "paypal": {
> "email": "customer@example.com"
> },
> "vault": true,
> "returnUrl": "https://example.com/checkout/return",
> "cancelUrl": "https://example.com/checkout/cancel"
>}'

The vault flag is only honored while vaulting is enabled on your Coinflow account β€” with vaulting disabled, the order proceeds as a normal checkout. The URL values above are examples only; in a popup/SDK integration PayPal never navigates to them, but they must still point at one of your merchant account’s whitelisted URLs β€” a redirect URL on any other origin is rejected with a 403.

Detecting a Vaulted Account

Fetch the customer with Get Customer and check the saved PayPal account for vaulted: true:

$curl --location 'https://api-sandbox.coinflow.cash/api/customer/v2' \
>--header 'accept: application/json' \
>--header 'x-coinflow-auth-session-key: <YOUR_SESSION_KEY>'

Response (truncated):

1{
2 "customer": {
3 "paypal": {
4 "type": "paypal",
5 "alias": "customer@example.com",
6 "token": "EXAMPLE_SAVED_ACCOUNT_TOKEN",
7 "vaulted": true
8 }
9 }
10}

The values above are examples only β€” read the real token from the API response. The vaulted flag is only returned while vaulting is enabled on your account (the vault token itself never leaves Coinflow’s servers); if the account is not vaulted, fall back to the standard saved checkout approval flow.

Collecting Fraud Data with FraudNet (Required)

Because a vaulted checkout has no PayPal approval step, PayPal requires browser fraud data to be collected on the checkout page instead. This is done with FraudNet, a small PayPal JavaScript library you embed on the page where the customer confirms the purchase. FraudNet gathers device and browser signals keyed by a client metadata ID (CMID) that you generate, and you pass that same CMID to the vaulted checkout so PayPal Risk can match the payment to the collected data.

The vaulted checkout endpoint requires clientMetadataId β€” requests without it are rejected with a 400.

If you use Coinflow’s prebuilt checkout UI or the CoinflowPayPalButton component, FraudNet is embedded and the CMID is sent automatically β€” you can skip this section. Direct API integrations must embed FraudNet themselves as described below.

1

Generate a CMID for the page view

Generate a random ID of up to 32 characters, unique per page view (a UUID with the dashes removed works well). You will use the same value in the FraudNet snippet and the checkout request.

1const clientMetadataId = crypto.randomUUID().replace(/-/g, '');
2

Embed the FraudNet snippet on your checkout page

Add the parameter block and the FraudNet script to the page where the customer confirms the vaulted purchase. The fncls attribute value is mandated by PayPal and must be exactly as shown.

1<script type="application/json" fncls="fnparams-dede7cc5-15fd-4c75-a9f4-36c430ee3a99">
2 {
3 "f": "EXAMPLE_CMID_REPLACE_WITH_YOURS",
4 "s": "YOUR_COMPANY_CHECKOUT_PAGE",
5 "sandbox": true
6 }
7</script>
8<script type="text/javascript" src="https://c.paypal.com/da/r/fb.js"></script>
  • f β€” the CMID you generated in the previous step.
  • s β€” a static label you define identifying the page (recommended format <YOUR_COMPANY>_<PAGE_NAME>, max 32 characters). The value above is an example only β€” choose your own.
  • sandbox β€” set true when testing against the Coinflow sandbox; omit it in production.

In a single-page app, inject both tags dynamically when the checkout page mounts instead of hardcoding them in the document head:

1function loadFraudNet({clientMetadataId, sandbox}) {
2 if (document.getElementById('fnparams')) return;
3
4 const params = document.createElement('script');
5 params.type = 'application/json';
6 params.id = 'fnparams';
7 params.setAttribute('fncls', 'fnparams-dede7cc5-15fd-4c75-a9f4-36c430ee3a99');
8 params.text = JSON.stringify({
9 f: clientMetadataId,
10 s: 'YOUR_COMPANY_CHECKOUT_PAGE',
11 ...(sandbox ? {sandbox: true} : {}),
12 });
13
14 const script = document.createElement('script');
15 script.type = 'text/javascript';
16 script.async = true;
17 script.src = 'https://c.paypal.com/da/r/fb.js';
18
19 document.head.appendChild(params);
20 document.head.appendChild(script);
21}
3

Pass the CMID on the vaulted checkout

Send the same value as clientMetadataId in the request body of the PayPal Vaulted Checkout call, as shown in the next section.

Generate a fresh CMID per page view (not per customer or per session) and load FraudNet as early as possible β€” the longer the snippet runs before the customer clicks pay, the more signal PayPal Risk has to approve the payment.

Charging a Vaulted Account

When the saved account is vaulted, call the PayPal Vaulted Checkout endpoint with the saved account’s token and the FraudNet clientMetadataId collected on the page.

$curl --location 'https://api-sandbox.coinflow.cash/api/checkout/paypal/vaulted/<YOUR_MERCHANT_ID>' \
>--header 'accept: application/json' \
>--header 'content-type: application/json' \
>--header 'x-coinflow-auth-session-key: <YOUR_SESSION_KEY>' \
>--data-raw '{
> "subtotal": {
> "cents": 500,
> "currency": "USD"
> },
> "token": "EXAMPLE_SAVED_ACCOUNT_TOKEN",
> "clientMetadataId": "EXAMPLE_CMID_REPLACE_WITH_YOURS"
>}'

Response:

1{
2 "paymentId": "EXAMPLE_PAYMENT_ID"
3}

By the time the response returns, the payment is already authorized β€” there is no approval link to follow and no PayPal SDK session to run.

Do not open the PayPal SDK approval flow for a vaulted checkout. The order is created already payer-approved, so there is nothing for the customer to approve β€” render your own β€œPay” button (or reuse the PayPal-branded button without starting a session) and treat a successful response as the completed purchase.

A clean integration pattern: when the customer’s saved PayPal account is vaulted, route the click to the vaulted endpoint and show your confirmation UI on success; otherwise run the standard saved-checkout approval flow. Coinflow’s prebuilt checkout UI and CoinflowPayPalButton component do exactly this automatically once vaulting is enabled on your account.

Confirming the Payment

Vaulted payments emit the same webhook lifecycle as every other PayPal payment, just without the waiting:

  • Authorized β€” sent immediately after the vaulted checkout succeeds.
  • Settled β€” sent when funds are delivered to your configured settlement location. This remains the recommended signal to fulfill the purchase.
  • Failure β€” sent if the charge is declined or blocked by risk checks.

See Checkout Webhooks for configuring your webhook endpoint.