Skip to main content
Glama

Advanced PocketBase MCP Server

stripe.js30.4 kB
import Stripe from 'stripe'; export class StripeService { stripe; pb; constructor(pb) { const stripeSecretKey = process.env.STRIPE_SECRET_KEY; if (!stripeSecretKey) { throw new Error('STRIPE_SECRET_KEY environment variable is required'); } this.stripe = new Stripe(stripeSecretKey, { apiVersion: '2023-10-16', }); this.pb = pb; } // Product Management async createProduct(data) { try { // Create product in Stripe const stripeProduct = await this.stripe.products.create({ name: data.name, description: data.description, metadata: data.metadata || {}, }); // Create price in Stripe const stripePrice = await this.stripe.prices.create({ unit_amount: data.price, currency: data.currency || 'usd', product: stripeProduct.id, recurring: data.recurring ? { interval: data.interval || 'month', } : undefined, }); // Save to PocketBase const productRecord = await this.pb.collection('stripe_products').create({ name: data.name, description: data.description, price: data.price, currency: data.currency || 'usd', recurring: data.recurring || false, interval: data.interval, stripeProductId: stripeProduct.id, stripePriceId: stripePrice.id, active: true, metadata: data.metadata || {}, }); return productRecord; } catch (error) { throw new Error(`Failed to create product: ${error.message}`); } } // Customer Management async createCustomer(data) { try { // Check if customer already exists const existingCustomer = await this.pb.collection('stripe_customers') .getFirstListItem(`email="${data.email}"`) .catch(() => null); if (existingCustomer) { return existingCustomer; } // Create customer in Stripe const stripeCustomer = await this.stripe.customers.create({ email: data.email, name: data.name, metadata: { userId: data.userId || '', ...data.metadata, }, }); // Save to PocketBase const customerRecord = await this.pb.collection('stripe_customers').create({ email: data.email, name: data.name, stripeCustomerId: stripeCustomer.id, userId: data.userId, metadata: data.metadata || {}, }); return customerRecord; } catch (error) { throw new Error(`Failed to create customer: ${error.message}`); } } // Create Payment Intent directly (for custom payment flows) async createPaymentIntent(data) { try { const paymentIntent = await this.stripe.paymentIntents.create({ amount: data.amount, currency: data.currency || 'usd', customer: data.customerId, description: data.description, metadata: data.metadata || {}, }); return { clientSecret: paymentIntent.client_secret, paymentIntentId: paymentIntent.id, }; } catch (error) { throw new Error(`Failed to create payment intent: ${error.message}`); } } // Retrieve customer information async retrieveCustomer(customerId) { try { const stripeCustomer = await this.stripe.customers.retrieve(customerId); return stripeCustomer; } catch (error) { throw new Error(`Failed to retrieve customer: ${error.message}`); } } // Update customer information async updateCustomer(customerId, data) { try { const stripeCustomer = await this.stripe.customers.update(customerId, { email: data.email, name: data.name, metadata: data.metadata, }); // Also update in PocketBase if exists try { const pbCustomer = await this.pb.collection('stripe_customers') .getFirstListItem(`stripeCustomerId="${customerId}"`); await this.pb.collection('stripe_customers').update(pbCustomer.id, { email: data.email || pbCustomer.email, name: data.name || pbCustomer.name, metadata: { ...pbCustomer.metadata, ...data.metadata }, }); } catch (error) { // Customer might not exist in PocketBase, that's ok console.warn('Could not update customer in PocketBase:', error); } return stripeCustomer; } catch (error) { throw new Error(`Failed to update customer: ${error.message}`); } } // Cancel subscription async cancelSubscription(subscriptionId, cancelAtPeriodEnd = false) { try { let stripeSubscription; if (cancelAtPeriodEnd) { stripeSubscription = await this.stripe.subscriptions.update(subscriptionId, { cancel_at_period_end: true, }); } else { stripeSubscription = await this.stripe.subscriptions.cancel(subscriptionId); } // Update in PocketBase try { const pbSubscription = await this.pb.collection('stripe_subscriptions') .getFirstListItem(`stripeSubscriptionId="${subscriptionId}"`); await this.pb.collection('stripe_subscriptions').update(pbSubscription.id, { status: stripeSubscription.status, cancelAtPeriodEnd: stripeSubscription.cancel_at_period_end, }); } catch (error) { console.warn('Could not update subscription in PocketBase:', error); } return stripeSubscription; } catch (error) { throw new Error(`Failed to cancel subscription: ${error.message}`); } } // Checkout Session async createCheckoutSession(data) { try { const sessionData = { line_items: [{ price: data.priceId, quantity: 1, }], mode: data.mode || 'payment', success_url: data.successUrl, cancel_url: data.cancelUrl, metadata: data.metadata || {}, }; if (data.customerId) { sessionData.customer = data.customerId; } else if (data.customerEmail) { sessionData.customer_email = data.customerEmail; } const session = await this.stripe.checkout.sessions.create(sessionData); if (!session.url) { throw new Error('Failed to create checkout session URL'); } return { url: session.url, sessionId: session.id, }; } catch (error) { throw new Error(`Failed to create checkout session: ${error.message}`); } } // Webhook Handler async handleWebhook(body, signature) { const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET; if (!webhookSecret) { throw new Error('STRIPE_WEBHOOK_SECRET environment variable is required'); } try { const event = this.stripe.webhooks.constructEvent(body, signature, webhookSecret); switch (event.type) { case 'checkout.session.completed': return await this.handleCheckoutCompleted(event.data.object); case 'invoice.payment_succeeded': return await this.handlePaymentSucceeded(event.data.object); case 'customer.subscription.created': case 'customer.subscription.updated': return await this.handleSubscriptionUpdated(event.data.object); case 'customer.subscription.deleted': return await this.handleSubscriptionDeleted(event.data.object); default: console.log(`Unhandled event type: ${event.type}`); return { received: true }; } } catch (error) { throw new Error(`Webhook error: ${error.message}`); } } async handleCheckoutCompleted(session) { try { // Create payment record if (session.amount_total && session.customer) { await this.pb.collection('stripe_payments').create({ customerId: session.customer, amount: session.amount_total, currency: session.currency, status: 'succeeded', stripePaymentIntentId: session.payment_intent || session.id, description: `Payment for session ${session.id}`, metadata: session.metadata || {}, }); } // Handle subscription if present if (session.subscription) { const subscription = await this.stripe.subscriptions.retrieve(session.subscription); await this.handleSubscriptionUpdated(subscription); } return { processed: true }; } catch (error) { console.error('Error handling checkout completed:', error); throw error; } } async handlePaymentSucceeded(invoice) { try { if (invoice.customer && invoice.amount_paid) { await this.pb.collection('stripe_payments').create({ customerId: invoice.customer, amount: invoice.amount_paid, currency: invoice.currency, status: 'succeeded', stripePaymentIntentId: invoice.payment_intent || invoice.id, description: `Invoice payment ${invoice.number}`, metadata: invoice.metadata || {}, }); } return { processed: true }; } catch (error) { console.error('Error handling payment succeeded:', error); throw error; } } async handleSubscriptionUpdated(subscription) { try { // Find existing subscription or create new one let subscriptionRecord; try { subscriptionRecord = await this.pb.collection('stripe_subscriptions') .getFirstListItem(`stripeSubscriptionId="${subscription.id}"`); // Update existing await this.pb.collection('stripe_subscriptions').update(subscriptionRecord.id, { status: subscription.status, currentPeriodStart: new Date(subscription.current_period_start * 1000).toISOString(), currentPeriodEnd: new Date(subscription.current_period_end * 1000).toISOString(), cancelAtPeriodEnd: subscription.cancel_at_period_end, }); } catch { // Create new subscription await this.pb.collection('stripe_subscriptions').create({ customerId: subscription.customer, stripeSubscriptionId: subscription.id, status: subscription.status, currentPeriodStart: new Date(subscription.current_period_start * 1000).toISOString(), currentPeriodEnd: new Date(subscription.current_period_end * 1000).toISOString(), cancelAtPeriodEnd: subscription.cancel_at_period_end, metadata: subscription.metadata || {}, }); } return { processed: true }; } catch (error) { console.error('Error handling subscription updated:', error); throw error; } } async handleSubscriptionDeleted(subscription) { try { const subscriptionRecord = await this.pb.collection('stripe_subscriptions') .getFirstListItem(`stripeSubscriptionId="${subscription.id}"`); await this.pb.collection('stripe_subscriptions').update(subscriptionRecord.id, { status: 'canceled', }); return { processed: true }; } catch (error) { console.error('Error handling subscription deleted:', error); throw error; } } // Sync products from Stripe to PocketBase async syncProducts() { try { const stripeProducts = await this.stripe.products.list({ active: true }); const results = []; for (const product of stripeProducts.data) { // Get prices for this product const prices = await this.stripe.prices.list({ product: product.id, active: true }); for (const price of prices.data) { try { // Check if product exists in PocketBase let existingProduct; try { existingProduct = await this.pb.collection('stripe_products') .getFirstListItem(`stripeProductId="${product.id}" && stripePriceId="${price.id}"`); } catch { existingProduct = null; } const productData = { name: product.name, description: product.description, price: price.unit_amount || 0, currency: price.currency, recurring: !!price.recurring, interval: price.recurring?.interval, stripeProductId: product.id, stripePriceId: price.id, active: product.active && price.active, metadata: { ...product.metadata, ...price.metadata }, }; if (existingProduct) { await this.pb.collection('stripe_products').update(existingProduct.id, productData); results.push({ action: 'updated', productId: product.id, priceId: price.id }); } else { await this.pb.collection('stripe_products').create(productData); results.push({ action: 'created', productId: product.id, priceId: price.id }); } } catch (error) { results.push({ action: 'error', productId: product.id, priceId: price.id, error: error.message }); } } } return { synced: results.length, results }; } catch (error) { throw new Error(`Failed to sync products: ${error.message}`); } } // === NEW MODERN STRIPE FEATURES === // Payment Methods Management async createPaymentMethod(data) { try { const paymentMethod = await this.stripe.paymentMethods.create({ type: data.type, card: data.card, billing_details: data.billing_details, metadata: data.metadata || {}, }); return paymentMethod; } catch (error) { throw new Error(`Failed to create payment method: ${error.message}`); } } async attachPaymentMethod(paymentMethodId, customerId) { try { const paymentMethod = await this.stripe.paymentMethods.attach(paymentMethodId, { customer: customerId, }); return paymentMethod; } catch (error) { throw new Error(`Failed to attach payment method: ${error.message}`); } } async detachPaymentMethod(paymentMethodId) { try { const paymentMethod = await this.stripe.paymentMethods.detach(paymentMethodId); return paymentMethod; } catch (error) { throw new Error(`Failed to detach payment method: ${error.message}`); } } async listPaymentMethods(customerId, type) { try { const paymentMethods = await this.stripe.paymentMethods.list({ customer: customerId, type: type || 'card', }); return paymentMethods; } catch (error) { throw new Error(`Failed to list payment methods: ${error.message}`); } } // Setup Intents for saving payment methods async createSetupIntent(data) { try { const setupIntent = await this.stripe.setupIntents.create({ customer: data.customerId, payment_method_types: data.paymentMethodTypes || ['card'], usage: data.usage || 'off_session', description: data.description, metadata: data.metadata || {}, }); return setupIntent; } catch (error) { throw new Error(`Failed to create setup intent: ${error.message}`); } } async confirmSetupIntent(setupIntentId, data) { try { const setupIntent = await this.stripe.setupIntents.confirm(setupIntentId, { payment_method: data.paymentMethod, return_url: data.returnUrl, }); return setupIntent; } catch (error) { throw new Error(`Failed to confirm setup intent: ${error.message}`); } } // Payment Links - Modern shareable payment links async createPaymentLink(data) { try { const paymentLink = await this.stripe.paymentLinks.create({ line_items: data.lineItems, metadata: data.metadata || {}, allow_promotion_codes: data.allowPromotionCodes, automatic_tax: data.automaticTax ? { enabled: true } : undefined, custom_text: data.customText, customer_creation: data.customerCreation || 'if_required', invoice_creation: data.invoiceCreation, phone_number_collection: data.phoneNumberCollection, shipping_address_collection: data.shippingAddressCollection, submit_type: data.submitType, subscription_data: data.subscriptionData, }); return paymentLink; } catch (error) { throw new Error(`Failed to create payment link: ${error.message}`); } } async retrievePaymentLink(paymentLinkId) { try { const paymentLink = await this.stripe.paymentLinks.retrieve(paymentLinkId); return paymentLink; } catch (error) { throw new Error(`Failed to retrieve payment link: ${error.message}`); } } async updatePaymentLink(paymentLinkId, data) { try { const paymentLink = await this.stripe.paymentLinks.update(paymentLinkId, { active: data.active, metadata: data.metadata, }); return paymentLink; } catch (error) { throw new Error(`Failed to update payment link: ${error.message}`); } } async listPaymentLinks(params = {}) { try { const paymentLinks = await this.stripe.paymentLinks.list({ active: params.active, limit: params.limit || 10, }); return paymentLinks; } catch (error) { throw new Error(`Failed to list payment links: ${error.message}`); } } // Financial Connections for bank account verification async createFinancialConnectionsSession(data) { try { const session = await this.stripe.financialConnections.sessions.create({ account_holder: { type: data.accountHolderType, }, permissions: data.permissions, filters: data.filtersCountryCode ? { countries: [data.filtersCountryCode], } : undefined, return_url: data.returnUrl, prefetch: data.prefetch, }); return session; } catch (error) { throw new Error(`Failed to create financial connections session: ${error.message}`); } } async retrieveFinancialConnectionsAccount(accountId) { try { const account = await this.stripe.financialConnections.accounts.retrieve(accountId); return account; } catch (error) { throw new Error(`Failed to retrieve financial connections account: ${error.message}`); } } async listFinancialConnectionsAccounts(sessionId) { try { const accounts = await this.stripe.financialConnections.accounts.list({ session: sessionId, }); return accounts; } catch (error) { throw new Error(`Failed to list financial connections accounts: ${error.message}`); } } // Enhanced Payment Intents with latest features async createEnhancedPaymentIntent(data) { try { const paymentIntent = await this.stripe.paymentIntents.create({ amount: data.amount, currency: data.currency || 'usd', customer: data.customerId, payment_method_types: data.paymentMethodTypes || ['card'], description: data.description, receipt_email: data.receiptEmail, setup_future_usage: data.setupFutureUsage, capture_method: data.captureMethod || 'automatic', confirmation_method: data.confirmationMethod || 'automatic', return_url: data.returnUrl, metadata: data.metadata || {}, application_fee_amount: data.applicationFeeAmount, transfer_data: data.transferData, statement_descriptor: data.statementDescriptor, statement_descriptor_suffix: data.statementDescriptorSuffix, }); return paymentIntent; } catch (error) { throw new Error(`Failed to create enhanced payment intent: ${error.message}`); } } async confirmPaymentIntent(paymentIntentId, data) { try { const paymentIntent = await this.stripe.paymentIntents.confirm(paymentIntentId, { payment_method: data.paymentMethod, return_url: data.returnUrl, receipt_email: data.receiptEmail, }); return paymentIntent; } catch (error) { throw new Error(`Failed to confirm payment intent: ${error.message}`); } } async capturePaymentIntent(paymentIntentId, amountToCapture) { try { const paymentIntent = await this.stripe.paymentIntents.capture(paymentIntentId, { amount_to_capture: amountToCapture, }); return paymentIntent; } catch (error) { throw new Error(`Failed to capture payment intent: ${error.message}`); } } // Subscription management with latest features async createAdvancedSubscription(data) { try { const subscription = await this.stripe.subscriptions.create({ customer: data.customerId, items: data.items, payment_behavior: data.paymentBehavior, payment_settings: data.paymentSettings, proration_behavior: data.prorationBehavior, collection_method: data.collectionMethod || 'charge_automatically', days_until_due: data.daysUntilDue, default_payment_method: data.defaultPaymentMethod, description: data.description, metadata: data.metadata || {}, promotion_code: data.promotionCode, trial_period_days: data.trialPeriodDays, trial_end: data.trialEnd, billing_cycle_anchor: data.billingCycleAnchor, }); // Save to PocketBase await this.pb.collection('stripe_subscriptions').create({ customerId: data.customerId, stripeSubscriptionId: subscription.id, status: subscription.status, currentPeriodStart: new Date(subscription.current_period_start * 1000).toISOString(), currentPeriodEnd: new Date(subscription.current_period_end * 1000).toISOString(), cancelAtPeriodEnd: subscription.cancel_at_period_end, metadata: subscription.metadata || {}, }); return subscription; } catch (error) { throw new Error(`Failed to create advanced subscription: ${error.message}`); } } // Refunds with enhanced features async createRefund(data) { try { const refund = await this.stripe.refunds.create({ payment_intent: data.paymentIntentId, charge: data.chargeId, amount: data.amount, reason: data.reason, refund_application_fee: data.refundApplicationFee, reverse_transfer: data.reverseTransfer, metadata: data.metadata || {}, }); return refund; } catch (error) { throw new Error(`Failed to create refund: ${error.message}`); } } // Coupons and Promotion Codes async createCoupon(data) { try { const coupon = await this.stripe.coupons.create({ id: data.id, duration: data.duration, amount_off: data.amountOff, percent_off: data.percentOff, currency: data.currency, duration_in_months: data.durationInMonths, max_redemptions: data.maxRedemptions, redeem_by: data.redeemBy, metadata: data.metadata || {}, }); return coupon; } catch (error) { throw new Error(`Failed to create coupon: ${error.message}`); } } async createPromotionCode(data) { try { const promotionCode = await this.stripe.promotionCodes.create({ coupon: data.couponId, code: data.code, customer: data.customerId, expires_at: data.expiresAt, max_redemptions: data.maxRedemptions, restrictions: data.restrictions, metadata: data.metadata || {}, }); return promotionCode; } catch (error) { throw new Error(`Failed to create promotion code: ${error.message}`); } } // Advanced Analytics and Reporting async getPaymentAnalytics(params = {}) { try { const charges = await this.stripe.charges.list({ created: { gte: params.startDate ? Math.floor(new Date(params.startDate).getTime() / 1000) : undefined, lte: params.endDate ? Math.floor(new Date(params.endDate).getTime() / 1000) : undefined, }, customer: params.customerId, limit: 100, }); const analytics = { totalAmount: 0, totalCount: charges.data.length, successfulPayments: 0, failedPayments: 0, refundedAmount: 0, currencies: {}, paymentMethods: {}, }; for (const charge of charges.data) { analytics.totalAmount += charge.amount; if (charge.status === 'succeeded') { analytics.successfulPayments++; } else if (charge.status === 'failed') { analytics.failedPayments++; } if (charge.refunded) { analytics.refundedAmount += charge.amount_refunded; } // Track currencies analytics.currencies[charge.currency] = (analytics.currencies[charge.currency] || 0) + charge.amount; // Track payment methods const paymentMethod = charge.payment_method_details?.type || 'unknown'; analytics.paymentMethods[paymentMethod] = (analytics.paymentMethods[paymentMethod] || 0) + 1; } return analytics; } catch (error) { throw new Error(`Failed to get payment analytics: ${error.message}`); } } } // Register tools function export function registerTools(server, pb) { server.tool('create_stripe_customer', 'Create a Stripe customer', { type: 'object', properties: { email: { type: 'string' }, name: { type: 'string' } } }, async (args) => { const stripeService = new StripeService(pb); const customer = await stripeService.createCustomer({ email: args.email, name: args.name }); return { success: true, customer }; }); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/DynamicEndpoints/advanced-pocketbase-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server