Guía de integración
This guide shows how to implement PayPal Checkout server-side using Node.js, Python, PHP, Java, .NET, or Ruby.
Aprenderás cómo:
Configurar las credenciales del entorno
Autenticar tu servidor con PayPal
Crear y capturar órdenes de manera segura desde tu backend
Descripción general
PayPal Checkout utiliza una integración en dos partes:
Cliente — Renderiza Smart Payment Buttons
Servidor — Gestiona llamadas API seguras (crear y capturar órdenes)
Esta guía se centra en la lógica del servidor usando la API Orders v2.
Prerrequisitos
Una cuenta de desarrollador de PayPal: https://developer.paypal.com/
Una aplicación sandbox creada en el Panel de control
Tu Client ID y Secret
Todos los ejemplos usan los endpoints de `sandbox`. Sustituye por `api.paypal.com` para producción.
Implementar PayPal Checkout
Configuración del entorno
[Inicia sesión](https://www.paypal.com/signin?returnUri=https%3A%2F%2Fdeveloper.paypal.com%2Fdashboard%2F%26intent%3Ddeveloper%26ctxId%3Dul1749482563440) para usar tus propias claves API en estos ejemplos.
Establece estos valores como variables de entorno o de forma segura en un archivo de configuración:
PAYPAL_CLIENT_ID=tu-client-id-sandbox
PAYPAL_CLIENT_SECRET=tu-client-secret-sandbox
PAYPAL_API=https://api-m.sandbox.paypal.com
Autenticación
Obtén un token de acceso de PayPal
curl -X POST https://api-m.sandbox.paypal.com/v1/oauth2/token \
-H "Accept: application/json" \
-H "Accept-Language: en_US" \
-u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
-d "grant_type=client_credentials"
const axios = require('axios');
async function generateAccessToken() {
const auth = Buffer.from(`${process.env.PAYPAL_CLIENT_ID}:${process.env.PAYPAL_CLIENT_SECRET}`).toString("base64");
const response = await axios.post(
`${process.env.PAYPAL_API}/v1/oauth2/token`,
"grant_type=client_credentials",
{
headers: {
Authorization: `Basic ${auth}`,
"Content-Type": "application/x-www-form-urlencoded",
},
}
);
return response.data.access_token;
}
import requests
import base64
import os
def generate_access_token():
client_id = os.getenv('PAYPAL_CLIENT_ID')
client_secret = os.getenv('PAYPAL_CLIENT_SECRET')
auth_string = f"{client_id}:{client_secret}"
auth_bytes = auth_string.encode('ascii')
auth_b64 = base64.b64encode(auth_bytes).decode('ascii')
headers = {
'Authorization': f'Basic {auth_b64}',
'Content-Type': 'application/x-www-form-urlencoded'
}
data = 'grant_type=client_credentials'
response = requests.post(
f"{os.getenv('PAYPAL_API')}/v1/oauth2/token",
headers=headers,
data=data
)
return response.json()['access_token']
import java.net.http.*;
import java.net.URI;
import java.util.Base64;
import com.fasterxml.jackson.databind.ObjectMapper;
public class PayPalAuth {
private static final String PAYPAL_API = System.getenv("PAYPAL_API");
private static final String CLIENT_ID = System.getenv("PAYPAL_CLIENT_ID");
private static final String CLIENT_SECRET = System.getenv("PAYPAL_CLIENT_SECRET");
public static String generateAccessToken() throws Exception {
String auth = CLIENT_ID + ":" + CLIENT_SECRET;
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(PAYPAL_API + "/v1/oauth2/token"))
.header("Authorization", "Basic " + encodedAuth)
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString("grant_type=client_credentials"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
ObjectMapper mapper = new ObjectMapper();
return mapper.readTree(response.body()).get("access_token").asText();
}
}
<?php
function generateAccessToken() {
$clientId = $_ENV['PAYPAL_CLIENT_ID'];
$clientSecret = $_ENV['PAYPAL_CLIENT_SECRET'];
$paypalApi = $_ENV['PAYPAL_API'];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $paypalApi . '/v1/oauth2/token');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, 'grant_type=client_credentials');
curl_setopt($ch, CURLOPT_USERPWD, $clientId . ':' . $clientSecret);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/x-www-form-urlencoded'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
return $data['access_token'];
}
?>
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
public class PayPalAuth
{
private static readonly HttpClient client = new HttpClient();
private static readonly string PayPalApi = Environment.GetEnvironmentVariable("PAYPAL_API");
private static readonly string ClientId = Environment.GetEnvironmentVariable("PAYPAL_CLIENT_ID");
private static readonly string ClientSecret = Environment.GetEnvironmentVariable("PAYPAL_CLIENT_SECRET");
public static async Task<string> GenerateAccessToken()
{
var authValue = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{ClientId}:{ClientSecret}"));
var request = new HttpRequestMessage(HttpMethod.Post, $"{PayPalApi}/v1/oauth2/token");
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authValue);
request.Content = new StringContent("grant_type=client_credentials", Encoding.UTF8, "application/x-www-form-urlencoded");
var response = await client.SendAsync(request);
var responseContent = await response.Content.ReadAsStringAsync();
var json = JObject.Parse(responseContent);
return json["access_token"].ToString();
}
}
require 'net/http'
require 'uri'
require 'base64'
require 'json'
def generate_access_token
client_id = ENV['PAYPAL_CLIENT_ID']
client_secret = ENV['PAYPAL_CLIENT_SECRET']
paypal_api = ENV['PAYPAL_API']
uri = URI("#{paypal_api}/v1/oauth2/token")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = "Basic #{Base64.strict_encode64("{client_id}:{client_secret}")}"
request['Content-Type'] = 'application/x-www-form-urlencoded'
request.body = 'grant_type=client_credentials'
response = http.request(request)
data = JSON.parse(response.body)
data['access_token']
end
Crear orden
Crea una nueva orden de PayPal
curl -X POST https://api-m.sandbox.paypal.com/v2/checkout/orders \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ACCESS_TOKEN" \
-d '{
"intent": "CAPTURE",
"purchase_units": [{
"amount": {
"currency_code": "USD",
"value": "10.00"
}
}]
}'
app.post("/api/orders", async (req, res) => {
const accessToken = await generateAccessToken();
const response = await axios.post(
`${process.env.PAYPAL_API}/v2/checkout/orders`,
{
intent: "CAPTURE",
purchase_units: [{ amount: { currency_code: "USD", value: "10.00" } }]
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
}
}
);
res.json({ id: response.data.id });
});
from flask import Flask, request, jsonify
import requests, os
app = Flask(__name__)
@app.route('/api/orders', methods=['POST'])
def create_order():
access_token = generate_access_token()
headers = {
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json'
}
order_data = {
'intent': 'CAPTURE',
'purchase_units': [{
'amount': {
'currency_code': 'USD',
'value': '10.00'
}
}]
}
response = requests.post(
f"{os.getenv('PAYPAL_API')}/v2/checkout/orders",
headers=headers,
json=order_data
)
return jsonify({'id': response.json()['id']})
@PostMapping("/api/orders")
public ResponseEntity<Map<String, String>> createOrder() throws Exception {
String accessToken = PayPalAuth.generateAccessToken();
String orderData = """
{
"intent": "CAPTURE",
"purchase_units": [{
"amount": {
"currency_code": "USD",
"value": "10.00"
}
}]
}
""";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(PAYPAL_API + "/v2/checkout/orders"))
.header("Authorization", "Bearer " + accessToken)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(orderData))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
ObjectMapper mapper = new ObjectMapper();
String orderId = mapper.readTree(response.body()).get("id").asText();
return ResponseEntity.ok(Map.of("id", orderId));
}
<?php
if (\$_SERVER['REQUEST_METHOD'] === 'POST' && \$_SERVER['REQUEST_URI'] === '/api/orders') {
\$accessToken = generateAccessToken();
\$orderData = [
'intent' => 'CAPTURE',
'purchase_units' => [[
'amount' => [
'currency_code' => 'USD',
'value' => '10.00'
]
]]
];
\$ch = curl_init();
curl_setopt(\$ch, CURLOPT_URL, \$_ENV['PAYPAL_API'] . '/v2/checkout/orders');
curl_setopt(\$ch, CURLOPT_POST, 1);
curl_setopt(\$ch, CURLOPT_POSTFIELDS, json_encode(\$orderData));
curl_setopt(\$ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . \$accessToken,
'Content-Type: application/json'
]);
curl_setopt(\$ch, CURLOPT_RETURNTRANSFER, true);
\$response = curl_exec(\$ch);
curl_close(\$ch);
\$data = json_decode(\$response, true);
echo json_encode(['id' => \$data['id']]);
}
?>
Capturar orden
Captura el pago de una orden aprobada
curl -X POST https://api-m.sandbox.paypal.com/v2/checkout/orders/ORDER_ID/capture \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ACCESS_TOKEN"
app.post("/api/orders/:orderID/capture", async (req, res) => {
const accessToken = await generateAccessToken();
const { orderID } = req.params;
const response = await axios.post(
`${process.env.PAYPAL_API}/v2/checkout/orders/${orderID}/capture`,
{},
{ headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" } }
);
res.json(response.data);
});
@app.route('/api/orders/<order_id>/capture', methods=['POST'])
def capture_order(order_id):
access_token = generate_access_token()
headers = { 'Authorization': f'Bearer {access_token}', 'Content-Type': 'application/json' }
response = requests.post(
f"{os.getenv('PAYPAL_API')}/v2/checkout/orders/{order_id}/capture",
headers=headers
)
return jsonify(response.json())
@PostMapping("/api/orders/{orderID}/capture")
public ResponseEntity<String> captureOrder(@PathVariable String orderID) throws Exception {
String accessToken = PayPalAuth.generateAccessToken();
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(PAYPAL_API + "/v2/checkout/orders/" + orderID + "/capture"))
.header("Authorization", "Bearer " + accessToken)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("{}"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
return ResponseEntity.ok(response.body());
}
<?php
if (preg_match('/^\/api\/orders\/([^\/]+)\/capture$/', \$_SERVER['REQUEST_URI'], \$matches)) {
\$orderID = \$matches[1];
\$accessToken = generateAccessToken();
\$ch = curl_init();
curl_setopt(\$ch, CURLOPT_URL, \$_ENV['PAYPAL_API'] . '/v2/checkout/orders/' . \$orderID . '/capture');
curl_setopt(\$ch, CURLOPT_POST, 1);
curl_setopt(\$ch, CURLOPT_POSTFIELDS, '{}');
curl_setopt(\$ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . \$accessToken,
'Content-Type: application/json'
]);
curl_setopt(\$ch, CURLOPT_RETURNTRANSFER, true);
\$response = curl_exec(\$ch);
curl_close(\$ch);
echo \$response;
}
?>
[HttpPost("api/orders/{orderID}/capture")]
public async Task<IActionResult> CaptureOrder(string orderID)
{
var accessToken = await PayPalAuth.GenerateAccessToken();
var request = new HttpRequestMessage(HttpMethod.Post, $"{PayPalApi}/v2/checkout/orders/{orderID}/capture");
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
request.Content = new StringContent("{}", Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
return Ok(await response.Content.ReadAsStringAsync());
}
Próximos pasos
Validar el estado del pago tras la captura
Almacenar los detalles del pago en tu base de datos
Añadir listeners de webhooks para manejar eventos asíncronos
Implementar manejo de errores y registro de logs
Añadir validación de órdenes y controles de seguridad
Enlaces útiles
Última actualización
¿Te fue útil?