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>>
Provider execution context with logger, config, and metadata
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>>
HTTP headers from the webhook request
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 instance for debug and error messages
Provider configuration (API keys, secrets, etc.)
Additional metadata (workspace ID, environment, etc.)
AsyncActionResult
Standard response wrapper for all provider methods:
The result data if successful, or null if failed
status
'success' | 'failed'
required
Operation status
Error details if status is "failed"
RevstackError
code
RevstackErrorCode
required
Standard error code (e.g., NotImplemented, InvalidInput, ProviderError)
Human-readable error message
PaginatedResult
Wrapper for paginated list responses:
Array of items for the current page
Whether there are more pages available
Cursor for the next page (for cursor-based pagination)
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.