PayPal
Vaulted payments
You can set up vaulted payments to:
- Save the payer’s PayPal payment method credentials for future transactions without processing an immediate payment.
- Set up recurring payments.
This guide explains how to use the Braintree GraphQL APIs to integrate PayPal vaulted payments into your application. Use this integration to:
- Provide faster payment processing for returning customers.
- Set up payment processing for subscriptions.
- Set up recurring billing (for example, automatic top-up or usage-based charges).
Workflow
The following diagram illustrates the process to create PayPal vaulted payments flow.

Create PayPal billing agreement
In your server-side code, use the
createPayPalBillingAgreement
mutation, as shown in the following sample, to initiate a PayPal billing agreement. Pass the following input fields and their values:
returnUrl
: The URL to which the payer is redirected after payer authentication, to approve the billing agreement.cancelUrl
: The URL to which the payer is redirected if they cancel the billing agreement.
returnUrl
and cancelUrl
use an approved hostname (such as, *.braintreegateway.com) and are HTTPS URLs.
On successful processing of createPayPalBillingAgreement
, Braintree
generates and sends an approval URL to your server. In your client-side code,
use the approval URL to redirect the payer to PayPal to authenticate and
authorize the billing agreement.
API Reference: createPayPalBillingAgreement
- Mutation
mutation CreatePayPalBillingAgreement(
$input: CreatePayPalBillingAgreementInput!
) {
createPayPalBillingAgreement(input: $input) {
billingAgreementToken
approvalUrl
}
}
- Variables
{ "input": { "merchantAccountId": "merchant_account_id", "returnUrl": "https://merchant_domain_name/paypal/returnURL", "cancelUrl": "https://merchant_domain_name/paypal/cancelURL" } }
Create PayPal billing agreement for recurring payments
To establish the terms of a recurring payment schedule, include the recurringBillingPlan
parameter in the createPayPalBillingAgreement
mutation. This allows you to define the billing cycle, frequency, and pricing. Pass the following input fields and their values:
recurringBillingPlan
(optional): Enables recurring payments when included.paypalExperienceProfile
(optional): Allows customization of the payer's experience.
Variables (Recurring billing agreement with subscription plan)
- Variables
{ "input": { "merchantAccountId": "merchant_account_id", "returnUrl": "https://merchant_domain_name/paypal/returnURL", "cancelUrl": "https://merchant_domain_name/paypal/cancelURL", "recurringBillingPlan": { "planType": "SUBSCRIPTION", "planMetadata": { "currencyCode": "USD", "totalAmount": "1.00", "billingCycles": [ { "trial": false, "billingCycleSequence": 1, "numberOfExecutions": 1, "frequencyInterval": { "billingFrequencyUnit": "MONTH", "billingFrequency": 1 }, "pricingScheme": { "pricingModel": "model", "price": "1.00" } } ] } } } }
Variables (Experience profile)
- Variables
{ "input": { "merchantAccountId": "merchant_account_id", "returnUrl": "https://merchant_domain_name/paypal/returnURL", "cancelUrl": "https://merchant_domain_name/paypal/cancelURL", "paypalExperienceProfile": { "collectShippingAddress": true, "shippingAddressEditable": false, "brandName": "Empire Co.", "landingPageType": "DEFAULT", "locale": "en-US" } } }
- Response
{ "data": { "createPayPalBillingAgreement": { "billingAgreementToken": "BA-9BJ56481CY242831B", "approvalUrl": "https://www.paypal.com/agreements/approve?ba_token=BA-9BJ56481CY242831B" } } }
Create transaction risk context
You can use the
createTransactionRiskContext
mutation to pass supplementary risk-related data to PayPal and create a transaction risk context that helps in risk management. On successful processing of createTransactionRiskContext
, PayPal returns a clientMetadataId
and a paypalRiskCorrelationId
.
Pass the clientMetadataId
in the chargePaymentMethod
mutation > riskData.deviceData.correlation_id
.
- Mutation
mutation CreateTransactionRiskContext(
$input: CreateTransactionRiskContextInput!
) {
createTransactionRiskContext(input: $input) {
clientMetadataId
paypalRiskCorrelationId
}
}
- Variables
{ "input": { "riskContext": { "fields": [ { "name": "sender_account_id", "value": "xyz123" }, { "name": "txn_count_total", "value": "15987" } ] } } }
- Response
{ "data": { "createTransactionRiskContext": { "clientMetadataId": "01e59aa07d2187e13b1bf9cf42a45596", "paypalRiskCorrelationId": "01e59aa07d2187e13b1bf9cf42a45596" } } }
Send payers to PayPal to approve billing agreement
From your server-side code, send the approval URL returned in the createPayPalBillingAgreement
response to your client-side code. In your client-side code, include the logic to redirect payers to the PayPal site where they can authorize the billing agreement. After the payer authorizes the billing agreement on the PayPal site:
- Payers are redirected to your
returnUrl
. - payment ID, payer ID, and payment token values are sent to the
onApprove
callback function in your server-side code.
Tokenize PayPal billing agreement
In your server-side code's onApprove
callback function, use the
tokenizePayPalBillingAgreement
mutation to convert the billing agreement into a tokenized payment method. Use the payment ID, payer ID, and payment token values received in the input payload of tokenizePayPalBillingAgreement
.
On successful processing of tokenizePayPalBillingAgreement
, Braintree/PayPal returns a paymentMethod.id
that represents the tokenized payment method.
API Reference:
tokenizePayPalBillingAgreement
- Mutation
mutation TokenizePayPalBillingAgreement(
$input: TokenizePayPalBillingAgreementInput!
) {
tokenizePayPalBillingAgreement(input: $input) {
paymentMethod {
id
details {
payerId
selectedFinancingOption {
term
monthlyPayment {
currencyCode
value
}
}
}
}
}
}
- Variables
{ "input": { "merchantAccountId": "merchant_account_id", "billingAgreement": { "billingAgreementToken": "BA-9BJ56481CY242831B" } } }
- Response
{ "data": { "tokenizePayPalBillingAgreement": { "paymentMethod": { "id": "tokencc_bj_hfqww2_rtyn7b_ggmjvs_6c9gd6_k9z", "details": { "payerId": "payer-id", "selectedFinancingOption": { "term": 12, "monthlyPayment": { "currencyCode": "GBP", "value": "10.0" } } } } } } }
Vault payment method
After tokenization, the payment method can be stored in the vault. Vaulting the payment method creates a persistent record of the payer's PayPal account, allowing you to initiate transactions without the payer's direct involvement.
vaultPaymentMethod
mutation, pass the paymentMethodId
in the input object. The paymentMethodId
is obtained from the tokenizePayPalBillingAgreement
or tokenizePayPalOneTimePayment
mutation.
API Reference:
vaultPaymentMethod
.
- Mutation
mutation VaultPaymentMethod($input: VaultPaymentMethodInput!) {
vaultPaymentMethod(input: $input) {
paymentMethod {
id
customer {
id
}
}
verification {
id
status
}
}
}
- Variables
{ "input": { "paymentMethodId": "tokencc_bj_hfqww2_rtyn7b_ggmjvs_6c9gd6_k9z" } }
- Response
{ "data": { "vaultPaymentMethod": { "paymentMethod": { "id": "cGF5bWVudG1ldGhvZF9jY182dnB3a20y", "customer": { "id": "Y3VzdG9tZXJfNjc3MDI1NzI1" } }, "verification": { "id": "dmVyaWZpY2F0aW9uXzliMHBhc2E3", "status": "VERIFIED" } } } }
Charge payment method
This final step uses the vaulted payment method to initiate a payment. In your server-side code, use the
chargePaymentMethod
mutation, referencing the vaulted payment method's ID and providing the transaction details.
clientMetadataId
returned from the createTransactionRiskContext
mutation in the chargePaymentMethod
mutation > riskData.deviceData.correlation_id
.
API Reference:
chargePaymentMethod
- Mutation
mutation ChargePaymentMethod($input: ChargePaymentMethodInput!) {
chargePaymentMethod(input: $input) {
transaction {
id
status
}
}
}
- Variables
{ "input": { "paymentMethodId": "cGF5bWVudG1ldGhvZF9jY182dnB3a20y", "transaction": { "amount": "10.00", "merchantAccountId": "canvaAU-AUD", "paymentInitiator": "RECURRING", "riskData": { "deviceData": "{\"correlation_id\": \"01e59aa07d2187e13b1bf9cf42a45596\"}" } } } }
- Response
{ "data": { "chargePaymentMethod": { "transaction": { "id": "dHJhbnNhY3Rpb25fZzVmNDY2djE", "status": "SETTLING" } } } }