PayPal MCP Server
by arbuthnot-eth
Verified
/// <reference types="@cloudflare/workers-types" />
import { WorkerEntrypoint } from 'cloudflare:workers'
import { ProxyToSelf } from 'workers-mcp'
interface PayPalConfig {
mode: 'sandbox' | 'live'
clientId: string
clientSecret: string
}
interface PayPalAccessToken {
access_token: string
token_type: string
expires_in: number
}
interface PayPalPayment {
intent: 'CAPTURE' | 'AUTHORIZE'
purchase_units: Array<{
amount: {
currency_code: string
value: string
}
description?: string
}>
application_context?: {
return_url: string
cancel_url: string
user_action?: string
}
}
interface CreatePaymentParams {
amount: string
currency?: string
description?: string
return_url?: string
cancel_url?: string
}
interface CapturePaymentParams {
orderId: string
}
export interface Env {
PAYPAL_CLIENT_ID: string
PAYPAL_CLIENT_SECRET: string
PAYPAL_MODE: 'sandbox' | 'live'
SHARED_SECRET: string
}
export default class MyWorker extends WorkerEntrypoint<Env> {
private paypalConfig: PayPalConfig | null = null
private async getPayPalConfig(): Promise<PayPalConfig> {
if (!this.paypalConfig) {
this.paypalConfig = {
mode: this.env.PAYPAL_MODE || 'sandbox',
clientId: this.env.PAYPAL_CLIENT_ID,
clientSecret: this.env.PAYPAL_CLIENT_SECRET
}
}
return this.paypalConfig
}
private async getAccessToken(): Promise<string> {
const config = await this.getPayPalConfig()
const credentials = btoa(`${config.clientId}:${config.clientSecret}`)
const response = await fetch(`https://api${config.mode === 'sandbox' ? '.sandbox' : ''}.paypal.com/v1/oauth2/token`, {
method: 'POST',
headers: {
'Authorization': `Basic ${credentials}`,
'Content-Type': 'application/x-www-form-urlencoded'
},
body: 'grant_type=client_credentials'
})
if (!response.ok) {
throw new Error('Failed to get PayPal access token')
}
const data: PayPalAccessToken = await response.json()
return data.access_token
}
/**
* Create a PayPal payment order
* @param {string} amount - The payment amount (e.g. "10.00")
* @param {string} [currency="USD"] - The currency code (e.g. "USD")
* @param {string} [description] - Optional description of the payment
* @returns {Promise<any>} The created payment order details including approval links
*/
async createPaypalOrder(amount: string, currency: string = 'USD', description?: string): Promise<any> {
try {
const accessToken = await this.getAccessToken()
const config = await this.getPayPalConfig()
const payment: PayPalPayment = {
intent: 'CAPTURE',
purchase_units: [{
amount: {
currency_code: currency,
value: amount
},
description: description
}],
application_context: {
return_url: 'https://paypal-mcp.imbibed.workers.dev/success',
cancel_url: 'https://paypal-mcp.imbibed.workers.dev/cancel',
user_action: 'PAY_NOW'
}
}
const response = await fetch(`https://api${config.mode === 'sandbox' ? '.sandbox' : ''}.paypal.com/v2/checkout/orders`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(payment)
})
const result = await response.json()
return {
content: [{
type: 'text',
text: JSON.stringify({
success: response.ok,
data: result
})
}]
}
} catch (error: any) {
return {
content: [{
type: 'text',
text: JSON.stringify({
success: false,
error: `Failed to create PayPal payment: ${error.message}`
})
}]
}
}
}
/**
* Capture a PayPal payment order
* @param {string} orderId - The PayPal order ID to capture
* @returns {Promise<any>} The capture details
*/
async capturePaypalOrder(orderId: string): Promise<any> {
try {
const accessToken = await this.getAccessToken()
const config = await this.getPayPalConfig()
const response = await fetch(`https://api${config.mode === 'sandbox' ? '.sandbox' : ''}.paypal.com/v2/checkout/orders/${orderId}/capture`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
})
const result = await response.json()
return {
content: [{
type: 'text',
text: JSON.stringify({
success: response.ok,
data: result
})
}]
}
} catch (error: any) {
return {
content: [{
type: 'text',
text: JSON.stringify({
success: false,
error: `Failed to capture PayPal payment: ${error.message}`
})
}]
}
}
}
// /**
// * A warm, friendly greeting from your new Workers MCP server.
// * @param {string} name - The name of the person we are greeting.
// * @returns {string} The contents of our greeting.
// */
// sayHello(name: string) {
// return `Hello from an MCP Worker, ${name}!`
// }
private corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
}
private jsonResponse(data: any, status: number = 200): Response {
return new Response(JSON.stringify(data), {
status,
headers: {
'Content-Type': 'application/json',
...this.corsHeaders,
},
})
}
/**
* @ignore
*/
async fetch(request: Request): Promise<Response> {
return new ProxyToSelf(this).fetch(request)
}
}