PayPal Sandbox MCP Server
by Horizon-CX
README.md
# PayPal Sandbox MCP Server
Servidor MCP remoto (Streamable HTTP) que expone dos tools de pago contra **PayPal Sandbox**, pensado exclusivamente para una demo de Salesforce Agentforce. **Sin autenticación en el endpoint `/mcp`** — solo apto para esta demo, nunca para producción.
## 1. Requisitos
- Node.js >= 20
- Una app de PayPal Developer (Sandbox) con `Client ID` y `Client Secret`
## 2. Instalación
```bash
cd paypal-mcp-server
npm install
```
## 3. Variables de entorno
Copia `.env.example` a `.env` y rellena tus credenciales de Sandbox:
```bash
cp .env.example .env
```
| Variable | Descripción | Por defecto |
|---|---|---|
| `PAYPAL_CLIENT_ID` | Client ID de tu app de PayPal Sandbox | *(obligatorio)* |
| `PAYPAL_CLIENT_SECRET` | Client Secret de tu app de PayPal Sandbox | *(obligatorio)* |
| `PAYPAL_ENVIRONMENT` | Debe ser `sandbox` | `sandbox` |
| `PUBLIC_BASE_URL` | URL pública donde se desplegará este servidor (se usa para `return_url`/`cancel_url`) | `http://localhost:3000` |
| `PORT` | Puerto HTTP | `3000` |
| `LOG_LEVEL` | Nivel de log de pino | `info` |
El servidor nunca registra `Client Secret`, tokens de acceso ni cabeceras `Authorization` (ver `src/logger.ts`).
## 4. Arrancar localmente
```bash
npm run dev
```
o compilado:
```bash
npm run build
npm start
```
## 5. Probar `GET /health`
```bash
curl http://localhost:3000/health
```
Respuesta esperada:
```json
{ "status": "UP", "environment": "sandbox", "mcpEndpoint": "/mcp" }
```
## 6. Probarlo con MCP Inspector
```bash
npx @modelcontextprotocol/inspector
```
En la UI del Inspector, elige transporte **Streamable HTTP** y apunta a `http://localhost:3000/mcp`. Deberías poder listar las tools `create_paypal_payment` y `finalize_paypal_payment` y ejecutarlas.
## 7. Despliegue
Cualquier hosting Node 20 sirve (Render, Railway, Fly.io, etc.). Con el `Dockerfile` incluido (multi-stage, usuario no root):
```bash
docker build -t paypal-mcp-server .
docker run -p 3000:3000 --env-file .env paypal-mcp-server
```
Este proyecto vive en el repo público [`Horizon-CX/PayPal-Sandbox-MCP-Server`](https://github.com/Horizon-CX/PayPal-Sandbox-MCP-Server) — puedes conectarlo directamente desde Render/Railway como origen de despliegue continuo.
## 8. Variables de entorno en el hosting
Configura en el panel del proveedor (Render/Railway/Fly.io):
- `PAYPAL_CLIENT_ID`
- `PAYPAL_CLIENT_SECRET`
- `PAYPAL_ENVIRONMENT=sandbox`
- `PUBLIC_BASE_URL` → la URL pública final asignada por el hosting (por ejemplo `https://paypal-mcp-demo.onrender.com`)
- `PORT` → normalmente la inyecta el propio hosting; déjala solo si el proveedor lo requiere explícito
## 9. Registrar el servidor en Salesforce
En Setup → MCP Servers → New:
| Campo | Valor |
|---|---|
| MCP Server Name | `PayPal Sandbox Payments` |
| Server URL | `https://<dominio-publico>/mcp` |
| Authentication Method | `No Authentication` |
> ⚠️ **"No Authentication" solo es aceptable para esta demo con PayPal Sandbox. No debe utilizarse así en producción.**
## 10. Tools a seleccionar
- `create_paypal_payment`
- `finalize_paypal_payment`
## 11. Ejemplos de input/output
### `create_paypal_payment`
Input:
```json
{
"salesforceOrderId": "801xx0000000001",
"orderNumber": "ORD-00001",
"amount": "49.99",
"currency": "EUR",
"description": "Pedido demo Agentforce"
}
```
Output (`structuredContent`):
```json
{
"success": true,
"salesforceOrderId": "801xx0000000001",
"orderNumber": "ORD-00001",
"paypalOrderId": "5O190127TN364715T",
"approvalUrl": "https://www.sandbox.paypal.com/checkoutnow?token=5O190127TN364715T",
"status": "CREATED",
"paid": false,
"amount": "49.99",
"currency": "EUR"
}
```
### `finalize_paypal_payment`
Input:
```json
{
"salesforceOrderId": "801xx0000000001",
"paypalOrderId": "5O190127TN364715T"
}
```
Output cuando el comprador ya aprobó y se captura correctamente:
```json
{
"success": true,
"salesforceOrderId": "801xx0000000001",
"paypalOrderId": "5O190127TN364715T",
"paypalStatus": "COMPLETED",
"paymentStatus": "PAID",
"paid": true,
"captureId": "3C679366NW308354M",
"amount": "49.99",
"currency": "EUR"
}
```
Output cuando el comprador aún no ha aprobado:
```json
{
"success": true,
"salesforceOrderId": "801xx0000000001",
"paypalOrderId": "5O190127TN364715T",
"paypalStatus": "CREATED",
"paymentStatus": "PENDING_CUSTOMER_APPROVAL",
"paid": false,
"amount": "49.99",
"currency": "EUR"
}
```
## 12. Advertencia
**`No Authentication` solo es aceptable para esta demo con PayPal Sandbox. No debe utilizarse así en producción.** Cualquier despliegue real de este patrón necesita autenticación en el endpoint `/mcp` (OAuth, API key, mTLS, etc.).
## 13. Crear un comprador PayPal Sandbox y aprobar el enlace
1. Entra en [developer.paypal.com](https://developer.paypal.com/dashboard/accounts) → **Sandbox → Accounts**.
2. Usa la cuenta personal (buyer) de sandbox que PayPal crea por defecto, o crea una nueva de tipo **Personal**.
3. Copia su email y contraseña de sandbox (botón "..." → **View/edit account** → **Profile**).
4. Abre el `approvalUrl` devuelto por `create_paypal_payment` en un navegador.
5. Inicia sesión con las credenciales del comprador sandbox del paso 3.
6. Aprueba el pago ficticio.
7. Verás la página `/paypal/return` de este servidor confirmando la aprobación.
8. Vuelve al chat de Agentforce y confirma que has pagado — esto debe disparar `finalize_paypal_payment`.
## Scripts
| Script | Descripción |
|---|---|
| `npm run dev` | Arranca en modo desarrollo con recarga (`tsx watch`) |
| `npm run build` | Compila TypeScript a `dist/` |
| `npm start` | Ejecuta el build compilado |
| `npm test` | Ejecuta los tests con Vitest |
| `npm run lint` | ESLint sobre `src` y `tests` |
| `npm run typecheck` | Comprueba tipos sin emitir (`src` + `tests`) |
## Arquitectura
```
src/
index.ts Bootstrap: config, servidor HTTP, graceful shutdown
config.ts Validación de variables de entorno (Zod)
logger.ts Logger pino con redacción de secretos
paypal/
paypalClient.ts Cliente PayPal: OAuth2 con cache de token, createOrder/getOrder/captureOrder
paypalTypes.ts Tipos de las respuestas de PayPal
paypalErrors.ts Errores de dominio (nunca exponen credenciales/tokens)
mcp/
createServer.ts Registro de las dos tools MCP
tools/
createPayPalPayment.ts
finalizePayPalPayment.ts
http/
app.ts Express: /health, /paypal/return, /paypal/cancel, /mcp (POST/GET/DELETE)
errorHandler.ts Middleware de errores centralizado
tests/
paypalClient.test.ts
createPayPalPayment.test.ts
finalizePayPalPayment.test.ts
```
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues