Skip to main content
The @revstackhq/providers-core package defines the core interfaces and types used to build billing provider integrations for Stripe, Paddle, Polar, and other payment platforms.

IProvider

The main interface that all billing provider implementations must satisfy. It extends multiple feature interfaces to provide a unified API across different providers.
interface IProvider extends
  IPaymentFeature,
  ISubscriptionFeature,
  ICheckoutFeature,
  ICustomerFeature,
  IBillingPortalFeature,
  IPaymentMethodFeature,
  ICatalogFeature

Core Methods

onInstall

Called when a provider is installed in a Revstack workspace. Used to set up webhooks, validate credentials, and perform initial configuration.
onInstall(
  ctx: ProviderContext,
  input: InstallInput
): Promise<AsyncActionResult<InstallResult>>
ctx
ProviderContext
required
Provider execution context with logger, config, and metadata
input
InstallInput
required
Installation configuration including credentials and webhook endpoints
Returns: AsyncActionResult<InstallResult> - Installation result with webhook URLs and metadata

onUninstall

Called when a provider is uninstalled. Used to clean up webhooks and resources.
onUninstall(
  ctx: ProviderContext,
  input: UninstallInput
): Promise<AsyncActionResult<boolean>>

verifyWebhookSignature

Verifies the authenticity of a webhook request from the provider.
verifyWebhookSignature(
  ctx: ProviderContext,
  payload: string | Buffer,
  headers: Record<string, string | string[] | undefined>,
  secret: string
): Promise<AsyncActionResult<boolean>>
payload
string | Buffer
required
Raw webhook payload body
headers
Record<string, string | string[] | undefined>
required
HTTP headers from the webhook request
secret
string
required
Webhook signing secret

parseWebhookEvent

Parses a provider-specific webhook payload into a normalized Revstack event.
parseWebhookEvent(
  ctx: ProviderContext,
  payload: any
): Promise<AsyncActionResult<RevstackEvent | null>>
Returns: RevstackEvent or null if the event should be ignored

getWebhookResponse

Returns the HTTP response to send back to the provider after processing a webhook.
getWebhookResponse(
  ctx: ProviderContext
): Promise<AsyncActionResult<WebhookResponse>>

BaseProvider

Abstract base class that provides default implementations for optional provider features. Extend this class to build a new provider integration.
abstract class BaseProvider implements IProvider {
  abstract readonly manifest: ProviderManifest;
  abstract onInstall(ctx: ProviderContext, input: InstallInput): Promise<AsyncActionResult<InstallResult>>;
  abstract onUninstall(ctx: ProviderContext, input: UninstallInput): Promise<AsyncActionResult<boolean>>;
  abstract verifyWebhookSignature(...): Promise<AsyncActionResult<boolean>>;
  abstract parseWebhookEvent(...): Promise<AsyncActionResult<RevstackEvent | null>>;
}
Optional methods: BaseProvider provides default “not implemented” responses for all feature methods. Override only the methods your provider supports.

Example Implementation

import { BaseProvider, ProviderManifest } from "@revstackhq/providers-core";

export class MyProvider extends BaseProvider {
  readonly manifest: ProviderManifest = {
    slug: "my-provider",
    name: "My Provider",
    version: "1.0.0",
    // ... other manifest fields
  };

  async onInstall(ctx, input) {
    // Set up webhooks, validate credentials
    return {
      data: { webhookId: "wh_123" },
      status: "success",
    };
  }

  async createCustomer(ctx, input) {
    // Create customer in provider's API
    const customerId = await this.api.customers.create(input);
    return {
      data: customerId,
      status: "success",
    };
  }

  // Override other methods as needed...
}

Feature Interfaces

IPaymentFeature

Methods for processing one-time payments:
createPayment
(ctx, input: CreatePaymentInput) => Promise<AsyncActionResult<string>>
Create a new payment. Returns the provider’s payment ID
getPayment
(ctx, input: GetPaymentInput) => Promise<AsyncActionResult<Payment>>
Retrieve payment details by ID
capturePayment
(ctx, input: CapturePaymentInput) => Promise<AsyncActionResult<string>>
Capture a previously authorized payment
refundPayment
(ctx, input: RefundPaymentInput) => Promise<AsyncActionResult<string>>
Refund a payment (full or partial). Returns the refund ID
listPayments
(ctx, options: ListPaymentsOptions) => Promise<AsyncActionResult<PaginatedResult<Payment>>>
List payments with pagination

ISubscriptionFeature

Methods for managing recurring subscriptions:
createSubscription
(ctx, input: CreateSubscriptionInput) => Promise<AsyncActionResult<string>>
Create a new subscription. Returns the provider’s subscription ID
getSubscription
(ctx, input: GetSubscriptionInput) => Promise<AsyncActionResult<Subscription>>
Retrieve subscription details by ID
updateSubscription
(ctx, input: UpdateSubscriptionInput) => Promise<AsyncActionResult<string>>
Update subscription (change plan, quantity, etc.)
cancelSubscription
(ctx, input: CancelSubscriptionInput) => Promise<AsyncActionResult<string>>
Cancel a subscription immediately or at period end
pauseSubscription
(ctx, input: PauseSubscriptionInput) => Promise<AsyncActionResult<string>>
Pause a subscription (if supported by provider)
resumeSubscription
(ctx, input: ResumeSubscriptionInput) => Promise<AsyncActionResult<string>>
Resume a paused subscription
listSubscriptions
(ctx, options: ListSubscriptionsOptions) => Promise<AsyncActionResult<PaginatedResult<Subscription>>>
List subscriptions with pagination

ICustomerFeature

Methods for managing customer records:
createCustomer
(ctx, input: CreateCustomerInput) => Promise<AsyncActionResult<string>>
Create a new customer. Returns the provider’s customer ID
getCustomer
(ctx, input: GetCustomerInput) => Promise<AsyncActionResult<Customer>>
Retrieve customer details by ID
updateCustomer
(ctx, input: UpdateCustomerInput) => Promise<AsyncActionResult<string>>
Update customer information (email, name, metadata, etc.)
deleteCustomer
(ctx, input: DeleteCustomerInput) => Promise<AsyncActionResult<boolean>>
Delete a customer record
listCustomers
(ctx, options: ListCustomersOptions) => Promise<AsyncActionResult<PaginatedResult<Customer>>>
List customers with pagination

ICheckoutFeature

Methods for creating hosted checkout experiences:
createCheckoutSession
(ctx, input: CheckoutSessionInput) => Promise<AsyncActionResult<CheckoutSessionResult>>
Create a hosted checkout session. Returns a URL to redirect the customer

IBillingPortalFeature

Methods for customer self-service portals:
createBillingPortalSession
(ctx, input: BillingPortalInput) => Promise<AsyncActionResult<BillingPortalResult>>
Create a billing portal session. Returns a URL where customers can manage subscriptions and payment methods

IPaymentMethodFeature

Methods for managing stored payment methods:
setupPaymentMethod
(ctx, input: SetupPaymentMethodInput) => Promise<AsyncActionResult<CheckoutSessionResult>>
Create a setup session to collect payment method details without charging
listPaymentMethods
(ctx, options: ListPaymentMethodsOptions) => Promise<AsyncActionResult<PaymentMethod[]>>
List payment methods for a customer
deletePaymentMethod
(ctx, input: DeletePaymentMethodInput) => Promise<AsyncActionResult<boolean>>
Detach/delete a payment method

ICatalogFeature

Methods for managing products and prices:
createProduct
(ctx, input: ProductInput) => Promise<AsyncActionResult<string>>
Create a product in the provider’s catalog
getProduct
(ctx, input: GetProductInput) => Promise<AsyncActionResult<Product>>
Retrieve product details by ID
updateProduct
(ctx, input: UpdateProductInput) => Promise<AsyncActionResult<string>>
Update product information
deleteProduct
(ctx, input: DeleteProductInput) => Promise<AsyncActionResult<boolean>>
Delete a product
listProducts
(ctx, options: ListProductsOptions) => Promise<AsyncActionResult<PaginatedResult<Product>>>
List products with pagination
createPrice
(ctx, input: PriceInput) => Promise<AsyncActionResult<string>>
Create a price for a product
getPrice
(ctx, input: GetPriceInput) => Promise<AsyncActionResult<Price>>
Retrieve price details by ID
listPrices
(ctx, options: ListPricesOptions) => Promise<AsyncActionResult<PaginatedResult<Price>>>
List prices with pagination

Core Types

ProviderContext

Context object passed to all provider methods:
logger
Logger
Logger instance for debug and error messages
config
Record<string, any>
Provider configuration (API keys, secrets, etc.)
metadata
Record<string, any>
Additional metadata (workspace ID, environment, etc.)

AsyncActionResult

Standard response wrapper for all provider methods:
data
T | null
required
The result data if successful, or null if failed
status
'success' | 'failed'
required
Operation status
error
RevstackError
Error details if status is "failed"

RevstackError

code
RevstackErrorCode
required
Standard error code (e.g., NotImplemented, InvalidInput, ProviderError)
message
string
required
Human-readable error message
details
any
Additional error context

PaginatedResult

Wrapper for paginated list responses:
items
T[]
required
Array of items for the current page
hasMore
boolean
required
Whether there are more pages available
nextCursor
string
Cursor for the next page (for cursor-based pagination)
total
number
Total count of items (if available)

Example: Creating a Provider

Here’s a minimal example of creating a custom provider:
import {
  BaseProvider,
  ProviderManifest,
  ProviderContext,
  InstallInput,
  InstallResult,
  AsyncActionResult,
  RevstackErrorCode,
} from "@revstackhq/providers-core";

export class CustomProvider extends BaseProvider {
  readonly manifest: ProviderManifest = {
    slug: "custom-provider",
    name: "Custom Provider",
    version: "1.0.0",
    status: "stable",
    categories: ["card"],
    description: "A custom billing provider integration",
    
    localization: {
      merchantCountries: ["US"],
      customerCountries: ["*"],
      processingCurrencies: ["USD"],
      settlementCurrencies: ["USD"],
    },
    
    compliance: {
      actsAsMoR: false,
      calculatesTaxes: false,
    },
    
    supportedPaymentMethods: ["card"],
    
    systemTraits: {
      hasNativeIdempotency: true,
      sandboxStrategy: "separate_credentials",
    },
    
    setup: {
      request: {
        apiKey: {
          label: "API Key",
          type: "password",
          secure: true,
          required: true,
          description: "Your provider API key",
        },
      },
    },
    
    capabilities: {
      payments: {
        create: true,
        capture: true,
        refund: true,
      },
      subscriptions: {
        create: true,
        update: true,
        cancel: true,
      },
    },
    
    media: {
      icon: "https://example.com/icon.png",
      logo: "https://example.com/logo.png",
    },
    
    links: {
      documentation: "https://docs.example.com",
    },
    
    engine: {
      revstack: "^1.0.0",
    },
    
    paginationType: "cursor",
  };

  async onInstall(
    ctx: ProviderContext,
    input: InstallInput
  ): Promise<AsyncActionResult<InstallResult>> {
    try {
      // Validate credentials
      const apiKey = input.config.apiKey;
      if (!apiKey) {
        return {
          data: null,
          status: "failed",
          error: {
            code: RevstackErrorCode.InvalidInput,
            message: "API key is required",
          },
        };
      }

      // Set up webhooks in provider's system
      const webhookUrl = input.webhookUrl;
      // ... webhook setup logic

      return {
        data: {
          webhookId: "wh_123",
          metadata: { setupComplete: true },
        },
        status: "success",
      };
    } catch (error) {
      return {
        data: null,
        status: "failed",
        error: {
          code: RevstackErrorCode.ProviderError,
          message: error.message,
        },
      };
    }
  }

  async onUninstall(ctx, input) {
    // Clean up webhooks
    return {
      data: true,
      status: "success",
    };
  }

  async verifyWebhookSignature(ctx, payload, headers, secret) {
    // Verify signature using provider's method
    const signature = headers["x-provider-signature"];
    const isValid = this.verifySignature(payload, signature, secret);
    
    return {
      data: isValid,
      status: "success",
    };
  }

  async parseWebhookEvent(ctx, payload) {
    // Parse provider webhook into RevstackEvent
    const eventType = payload.type;
    
    if (eventType === "charge.succeeded") {
      return {
        data: {
          type: "payment.succeeded",
          id: payload.id,
          data: payload.data,
        },
        status: "success",
      };
    }

    return {
      data: null,
      status: "success",
    };
  }

  async createCustomer(ctx, input) {
    const apiKey = ctx.config.apiKey;
    
    // Call provider API
    const customer = await fetch("https://api.provider.com/customers", {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${apiKey}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        email: input.email,
        name: input.name,
      }),
    }).then(r => r.json());

    return {
      data: customer.id,
      status: "success",
    };
  }

  // Implement other methods as needed...
}

Best Practices

Idempotency: If your provider doesn’t support native idempotency, set systemTraits.hasNativeIdempotency: false in the manifest. Revstack will handle idempotency for you.
Error handling: Always return AsyncActionResult with proper error codes. Never throw exceptions from provider methods.
Sensitive data: Mark sensitive configuration fields as secure: true in the manifest’s setup.request. These values will be encrypted at rest.

Build docs developers (and LLMs) love