Add Activepieces integration for workflow automation

- Add Activepieces fork with SmoothSchedule custom piece
- Create integrations app with Activepieces service layer
- Add embed token endpoint for iframe integration
- Create Automations page with embedded workflow builder
- Add sidebar visibility fix for embed mode
- Add list inactive customers endpoint to Public API
- Include SmoothSchedule triggers: event created/updated/cancelled
- Include SmoothSchedule actions: create/update/cancel events, list resources/services/customers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
poduck
2025-12-18 22:59:37 -05:00
parent 9848268d34
commit 3aa7199503
16292 changed files with 1284892 additions and 4708 deletions

View File

@@ -0,0 +1,34 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { stripeAuth } from '../..';
import { stripeCommon, getClient } from '../common';
export const stripeCancelSubscription = createAction({
name: 'cancel_subscription',
auth: stripeAuth,
displayName: 'Cancel Subscription',
description:
'Cancel an existing subscription, either immediately or at the end of the current billing period.',
props: {
subscription: stripeCommon.subscription,
cancel_at_period_end: Property.Checkbox({
displayName: 'Cancel at Period End',
description:
'If true, the subscription remains active until the end of the current billing period. If false, it cancels immediately.',
required: false,
defaultValue: false,
}),
},
async run(context) {
const { subscription, cancel_at_period_end } = context.propsValue;
const client = getClient(context.auth.secret_text);
if (cancel_at_period_end) {
return await client.subscriptions.update(subscription, {
cancel_at_period_end: true,
});
} else {
return await client.subscriptions.cancel(subscription);
}
},
});

View File

@@ -0,0 +1,80 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
import { stripeAuth } from '../..';
export const stripeCreateCustomer = createAction({
name: 'create_customer',
auth: stripeAuth,
displayName: 'Create Customer',
description: 'Create a customer in stripe',
props: {
email: Property.ShortText({
displayName: 'Email',
description: undefined,
required: true,
}),
name: Property.ShortText({
displayName: 'Name',
required: true,
}),
description: Property.LongText({
displayName: 'Description',
required: false,
}),
phone: Property.ShortText({
displayName: 'Phone',
required: false,
}),
line1: Property.ShortText({
displayName: 'Address Line 1',
required: false,
}),
postal_code: Property.ShortText({
displayName: 'Postal Code',
required: false,
}),
city: Property.ShortText({
displayName: 'City',
required: false,
}),
state: Property.ShortText({
displayName: 'State',
required: false,
}),
country: Property.ShortText({
displayName: 'Country',
required: false,
}),
},
async run(context) {
const customer = {
email: context.propsValue.email,
name: context.propsValue.name,
description: context.propsValue.description,
phone: context.propsValue.phone,
address: {
line1: context.propsValue.line1,
postal_code: context.propsValue.postal_code,
city: context.propsValue.city,
state: context.propsValue.state,
country: context.propsValue.country,
},
};
const response = await httpClient.sendRequest({
method: HttpMethod.POST,
url: 'https://api.stripe.com/v1/customers',
headers: {
Authorization: 'Bearer ' + context.auth.secret_text,
'Content-Type': 'application/x-www-form-urlencoded',
},
body: {
email: customer.email,
name: customer.name,
description: customer.description,
phone: customer.phone,
address: customer.address,
},
});
return response.body;
},
});

View File

@@ -0,0 +1,49 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
import { stripeAuth } from '../..';
export const stripeCreateInvoice = createAction({
name: 'create_invoice',
auth: stripeAuth,
displayName: 'Create Invoice',
description: 'Create an Invoice in stripe',
props: {
customer_id: Property.ShortText({
displayName: 'Customer ID',
description: 'Stripe Customer ID',
required: true,
}),
currency: Property.ShortText({
displayName: 'Currency',
description: 'Currency for the invoice (e.g., USD)',
required: true,
}),
description: Property.LongText({
displayName: 'Description',
description: 'Description for the invoice',
required: false,
}),
},
async run(context) {
const invoice = {
customer: context.propsValue.customer_id,
currency: context.propsValue.currency,
description: context.propsValue.description,
};
const response = await httpClient.sendRequest({
method: HttpMethod.POST,
url: 'https://api.stripe.com/v1/invoices',
headers: {
Authorization: 'Bearer ' + context.auth.secret_text,
'Content-Type': 'application/x-www-form-urlencoded',
},
body: {
customer: invoice.customer,
currency: invoice.currency,
description: invoice.description,
},
});
return response.body;
},
});

View File

@@ -0,0 +1,124 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
httpClient,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { stripeAuth } from '../..';
import { stripeCommon } from '../common';
export const stripeCreatePaymentIntent = createAction({
name: 'create_payment_intent',
auth: stripeAuth,
displayName: 'Create Payment (Payment Intent)',
description: 'Creates a new payment intent to start a payment flow.',
props: {
amount: Property.Number({
displayName: 'Amount',
description:
'The amount to charge, in a decimal format (e.g., 10.50 for $10.50).',
required: true,
}),
currency: Property.StaticDropdown({
displayName: 'Currency',
description: 'The three-letter ISO code for the currency.',
required: true,
options: {
options: [
{ label: 'US Dollar', value: 'usd' },
{ label: 'Euro', value: 'eur' },
{ label: 'Pound Sterling', value: 'gbp' },
{ label: 'Australian Dollar', value: 'aud' },
{ label: 'Canadian Dollar', value: 'cad' },
{ label: 'Swiss Franc', value: 'chf' },
{ label: 'Chinese Yuan', value: 'cny' },
{ label: 'Japanese Yen', value: 'jpy' },
{ label: 'Indian Rupee', value: 'inr' },
{ label: 'Singapore Dollar', value: 'sgd' },
],
},
}),
customer: stripeCommon.customer,
payment_method: Property.ShortText({
displayName: 'Payment Method ID',
description:
'The ID of the Payment Method to attach (e.g., `pm_...`). Required if you want to confirm the payment immediately.',
required: false,
}),
confirm: Property.Checkbox({
displayName: 'Confirm Payment Immediately',
description:
'If true, Stripe will attempt to charge the provided payment method. A `Payment Method ID` is required.',
required: false,
defaultValue: false,
}),
return_url: Property.ShortText({
displayName: 'Return URL',
description:
'The URL to redirect your customer back to after they authenticate their payment. Required when confirming the payment.',
required: false,
}),
description: Property.LongText({
displayName: 'Description',
required: false,
}),
receipt_email: Property.ShortText({
displayName: 'Receipt Email',
description:
"The email address to send a receipt to. This will override the customer's email address.",
required: false,
}),
},
async run(context) {
const {
amount,
currency,
customer,
payment_method,
confirm,
return_url,
description,
receipt_email,
} = context.propsValue;
if (confirm && !payment_method) {
throw new Error(
"A Payment Method ID is required when 'Confirm Payment' is set to true."
);
}
if (confirm && !return_url) {
throw new Error(
"A Return URL is required when 'Confirm Payment' is set to true."
);
}
const amountInCents = Math.round(amount * 100);
const body: { [key: string]: unknown } = {
amount: amountInCents,
currency: currency,
};
if (customer) body.customer = customer;
if (payment_method) body.payment_method = payment_method;
if (confirm) body.confirm = confirm;
if (return_url) body.return_url = return_url;
if (description) body.description = description;
if (receipt_email) body.receipt_email = receipt_email;
const response = await httpClient.sendRequest({
method: HttpMethod.POST,
url: `${stripeCommon.baseUrl}/payment_intents`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.secret_text,
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: body,
});
return response.body;
},
});

View File

@@ -0,0 +1,102 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { stripeAuth } from '../..';
import { getClient } from '../common';
import { Stripe } from 'stripe';
export const stripeCreatePaymentLink = createAction({
name: 'create_payment_link',
auth: stripeAuth,
displayName: 'Create Payment Link',
description:
'Creates a shareable, Stripe-hosted payment link for one-time purchases or subscriptions.',
props: {
line_items: Property.Array({
displayName: 'Line Items',
description:
'The products and quantities to include in the payment link.',
required: true,
properties: {
price: Property.ShortText({
displayName: 'Price ID',
description:
'The ID of the price object (e.g., price_1J2X3Y4Z...). Find this in your Stripe Dashboard under Products.',
required: true,
}),
quantity: Property.Number({
displayName: 'Quantity',
required: true,
}),
},
}),
after_completion_type: Property.StaticDropdown({
displayName: 'After Completion Behavior',
description:
"Controls the behavior after the purchase is complete. Defaults to showing Stripe's hosted confirmation page.",
required: false,
options: {
options: [
{ label: 'Show Confirmation Page', value: 'hosted_confirmation' },
{ label: 'Redirect to URL', value: 'redirect' },
],
},
}),
after_completion_redirect_url: Property.ShortText({
displayName: 'Redirect URL',
description:
'The URL to redirect the customer to after a successful purchase. Only used if the behavior is set to "Redirect to URL".',
required: false,
}),
allow_promotion_codes: Property.Checkbox({
displayName: 'Allow Promotion Codes',
description:
'Enables the user to enter a promotion code on the Payment Link page.',
required: false,
}),
billing_address_collection: Property.StaticDropdown({
displayName: 'Billing Address Collection',
description:
'Describes whether Checkout should collect the customers billing address.',
required: false,
options: {
options: [
{ label: 'Auto', value: 'auto' },
{ label: 'Required', value: 'required' },
],
},
}),
metadata: Property.Json({
displayName: 'Metadata',
required: false,
}),
},
async run(context) {
const client = getClient(context.auth.secret_text);
const props = context.propsValue;
const params: Stripe.PaymentLinkCreateParams = {
line_items: props.line_items as { price: string; quantity: number }[],
allow_promotion_codes: props.allow_promotion_codes,
billing_address_collection: props.billing_address_collection as
| 'auto'
| 'required'
| undefined,
metadata: props.metadata as Record<string, string> | undefined,
};
if (props.after_completion_type) {
params.after_completion = {
type: props.after_completion_type as 'hosted_confirmation' | 'redirect',
};
if (
props.after_completion_type === 'redirect' &&
props.after_completion_redirect_url
) {
params.after_completion.redirect = {
url: props.after_completion_redirect_url,
};
}
}
return await client.paymentLinks.create(params);
},
});

View File

@@ -0,0 +1,105 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
httpClient,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { stripeAuth } from '../..';
import { stripeCommon } from '../common';
export const stripeCreatePrice = createAction({
name: 'create_price',
auth: stripeAuth,
displayName: 'Create Price',
description:
'Create a price (one-time or recurring), associated with a product.',
props: {
product: stripeCommon.product,
unit_amount: Property.Number({
displayName: 'Unit Amount',
description:
'The price amount as a decimal, for example, 25.50 for $25.50.',
required: true,
}),
currency: Property.StaticDropdown({
displayName: 'Currency',
description: 'The three-letter ISO code for the currency.',
required: true,
options: {
options: [
{ label: 'US Dollar', value: 'usd' },
{ label: 'Euro', value: 'eur' },
{ label: 'Pound Sterling', value: 'gbp' },
{ label: 'Indian Rupee', value: 'inr' },
{ label: 'Australian Dollar', value: 'aud' },
{ label: 'Canadian Dollar', value: 'cad' },
{ label: 'Swiss Franc', value: 'chf' },
{ label: 'Chinese Yuan', value: 'cny' },
{ label: 'Japanese Yen', value: 'jpy' },
{ label: 'Singapore Dollar', value: 'sgd' },
],
},
}),
recurring_interval: Property.StaticDropdown({
displayName: 'Billing Interval',
description:
"Specify the billing frequency. Select 'One-Time' for a single, non-recurring payment.",
required: true,
defaultValue: 'one_time',
options: {
options: [
{ label: 'One-Time', value: 'one_time' },
{ label: 'Daily', value: 'day' },
{ label: 'Weekly', value: 'week' },
{ label: 'Monthly', value: 'month' },
{ label: 'Yearly', value: 'year' },
],
},
}),
recurring_interval_count: Property.Number({
displayName: 'Interval Count',
description:
'The number of intervals between subscription billings (e.g., for billing every 3 months, set Interval to Monthly and Interval Count to 3). Only used for recurring prices.',
required: false,
}),
},
async run(context) {
const {
product,
unit_amount,
currency,
recurring_interval,
recurring_interval_count,
} = context.propsValue;
const unitAmountInCents = Math.round(unit_amount * 100);
const body: { [key: string]: unknown } = {
product: product,
unit_amount: unitAmountInCents,
currency: currency,
};
if (recurring_interval !== 'one_time') {
body['recurring[interval]'] = recurring_interval;
if (recurring_interval_count) {
body['recurring[interval_count]'] = recurring_interval_count;
}
}
const response = await httpClient.sendRequest({
method: HttpMethod.POST,
url: `${stripeCommon.baseUrl}/prices`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.secret_text,
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: body,
});
return response.body;
},
});

View File

@@ -0,0 +1,89 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
httpClient,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { stripeAuth } from '../..';
import { stripeCommon } from '../common';
export const stripeCreateProduct = createAction({
name: 'create_product',
auth: stripeAuth,
displayName: 'Create Product',
description: 'Create a new product object in Stripe.',
props: {
name: Property.ShortText({
displayName: 'Product Name',
description:
'The products name, meant to be displayable to the customer.',
required: true,
}),
description: Property.LongText({
displayName: 'Description',
description:
'The products description, meant to be displayable to the customer.',
required: false,
}),
active: Property.Checkbox({
displayName: 'Active',
description:
'Whether the product is currently available for purchase. Defaults to true.',
required: false,
}),
images: Property.Array({
displayName: 'Image URLs',
description: 'A list of up to 8 URLs of images for this product.',
required: false,
}),
url: Property.ShortText({
displayName: 'Product URL',
description: 'A publicly-accessible online page for this product.',
required: false,
}),
metadata: Property.Json({
displayName: 'Metadata',
description:
'A set of key-value pairs to store additional information about the product.',
required: false,
}),
},
async run(context) {
const { name, description, active, images, url, metadata } =
context.propsValue;
const body: { [key: string]: unknown } = {
name: name,
};
if (description) body.description = description;
if (active !== undefined) body.active = active;
if (url) body.url = url;
if (images && Array.isArray(images)) {
images.forEach((image, index) => {
body[`images[${index}]`] = image;
});
}
if (metadata && typeof metadata === 'object') {
Object.keys(metadata).forEach((key) => {
body[`metadata[${key}]`] = (metadata as Record<string, string>)[key];
});
}
const response = await httpClient.sendRequest({
method: HttpMethod.POST,
url: `${stripeCommon.baseUrl}/products`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.secret_text,
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: body,
});
return response.body;
},
});

View File

@@ -0,0 +1,76 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
httpClient,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { stripeAuth } from '../..';
import { stripeCommon } from '../common';
export const stripeCreateRefund = createAction({
name: 'create_refund',
auth: stripeAuth,
displayName: 'Create a Refund',
description: 'Create a full or partial refund for a payment.',
props: {
payment_intent: stripeCommon.paymentIntent,
amount: Property.Number({
displayName: 'Amount',
description:
'The amount to refund (e.g., 12.99). If left blank, a full refund will be issued.',
required: false,
}),
reason: Property.StaticDropdown({
displayName: 'Reason',
description: 'An optional reason for the refund.',
required: false,
options: {
options: [
{ label: 'Duplicate', value: 'duplicate' },
{ label: 'Fraudulent', value: 'fraudulent' },
{ label: 'Requested by Customer', value: 'requested_by_customer' },
],
},
}),
metadata: Property.Json({
displayName: 'Metadata',
description:
'A set of key-value pairs to store additional information about the refund.',
required: false,
}),
},
async run(context) {
const { payment_intent, amount, reason, metadata } = context.propsValue;
const body: { [key: string]: unknown } = {
payment_intent: payment_intent,
};
if (amount !== undefined && amount !== null) {
body.amount = Math.round(amount * 100);
}
if (reason) {
body.reason = reason;
}
if (metadata && typeof metadata === 'object') {
Object.keys(metadata).forEach((key) => {
body[`metadata[${key}]`] = (metadata as Record<string, string>)[key];
});
}
const response = await httpClient.sendRequest({
method: HttpMethod.POST,
url: `${stripeCommon.baseUrl}/refunds`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.secret_text,
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: body,
});
return response.body;
},
});

View File

@@ -0,0 +1,123 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
httpClient,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { stripeAuth } from '../..';
import { stripeCommon } from '../common';
export const stripeCreateSubscription = createAction({
name: 'create_subscription',
auth: stripeAuth,
displayName: 'Create Subscription',
description:
'Start a subscription for a customer with specified items/prices.',
props: {
customer: stripeCommon.customer,
items: Property.Array({
displayName: 'Subscription Items',
description: 'A list of prices to subscribe the customer to.',
required: true,
properties: {
price: Property.ShortText({
displayName: 'Price ID',
description:
'The ID of the price object (e.g., price_...). You can find this in your Stripe Dashboard under Products.',
required: true,
}),
quantity: Property.Number({
displayName: 'Quantity',
description:
'The number of units of this price to subscribe to. Defaults to 1.',
required: false,
}),
},
}),
collection_method: Property.StaticDropdown({
displayName: 'Collection Method',
description:
"How to collect payment. 'charge_automatically' will try to bill the default payment method. 'send_invoice' will email an invoice.",
required: false,
options: {
options: [
{ label: 'Charge Automatically', value: 'charge_automatically' },
{ label: 'Send Invoice', value: 'send_invoice' },
],
},
}),
days_until_due: Property.Number({
displayName: 'Days Until Due',
description:
"Number of days before an invoice is due. Required if Collection Method is 'Send Invoice'.",
required: false,
}),
trial_period_days: Property.Number({
displayName: 'Trial Period (Days)',
description:
'Integer representing the number of trial days the customer receives before the subscription bills for the first time.',
required: false,
}),
default_payment_method: Property.ShortText({
displayName: 'Default Payment Method ID',
description:
'ID of the default payment method for the subscription (e.g., `pm_...`).',
required: false,
}),
metadata: Property.Json({
displayName: 'Metadata',
required: false,
}),
},
async run(context) {
const {
customer,
items,
collection_method,
days_until_due,
trial_period_days,
default_payment_method,
metadata,
} = context.propsValue;
const body: Record<string, unknown> = {
customer,
collection_method,
days_until_due,
trial_period_days,
default_payment_method,
metadata,
};
Object.keys(body).forEach((key) => {
if (body[key] === undefined || body[key] === null) {
delete body[key];
}
});
if (items && Array.isArray(items)) {
items.forEach((item, index) => {
const typedItem = item as { price: string; quantity?: number };
body[`items[${index}][price]`] = typedItem.price;
if (typedItem.quantity) {
body[`items[${index}][quantity]`] = typedItem.quantity;
}
});
}
const response = await httpClient.sendRequest({
method: HttpMethod.POST,
url: `${stripeCommon.baseUrl}/subscriptions`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.secret_text,
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: body,
});
return response.body;
},
});

View File

@@ -0,0 +1,21 @@
import { createAction } from '@activepieces/pieces-framework';
import { stripeAuth } from '../..';
import { stripeCommon, getClient } from '../common';
export const stripeDeactivatePaymentLink = createAction({
name: 'deactivate_payment_link',
auth: stripeAuth,
displayName: 'Deactivate Payment Link',
description:
'Disable or deactivate a Payment Link so it can no longer be used.',
props: {
payment_link_id: stripeCommon.paymentLink,
},
async run(context) {
const { payment_link_id } = context.propsValue;
const client = getClient(context.auth.secret_text);
return await client.paymentLinks.update(payment_link_id, {
active: false,
});
},
});

View File

@@ -0,0 +1,32 @@
import { createAction } from '@activepieces/pieces-framework';
import {
httpClient,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { stripeAuth } from '../..';
import { stripeCommon } from '../common';
export const stripeFindInvoice = createAction({
name: 'find_invoice',
auth: stripeAuth,
displayName: 'Find Invoice',
description: 'Finds an invoice by its unique ID.',
props: {
invoice_id: stripeCommon.invoice,
},
async run(context) {
const { invoice_id } = context.propsValue;
const response = await httpClient.sendRequest({
method: HttpMethod.GET,
url: `${stripeCommon.baseUrl}/invoices/${invoice_id}`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.secret_text,
},
});
return response.body;
},
});

View File

@@ -0,0 +1,37 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
AuthenticationType,
httpClient,
HttpMethod,
} from '@activepieces/pieces-common';
import { stripeAuth } from '../..';
export const stripeRetrieveCustomer = createAction({
name: 'retrieve_customer',
auth: stripeAuth,
displayName: 'Retrieve Customer',
description: 'Retrieve a customer in stripe by id',
props: {
id: Property.ShortText({
displayName: 'ID',
description: undefined,
required: true,
}),
},
async run(context) {
const customer = {
id: context.propsValue.id,
};
const response = await httpClient.sendRequest({
method: HttpMethod.GET,
url: `https://api.stripe.com/v1/customers/${customer.id}`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.secret_text,
},
});
return response.body;
},
});

View File

@@ -0,0 +1,32 @@
import { createAction } from '@activepieces/pieces-framework';
import {
httpClient,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { stripeAuth } from '../..';
import { stripeCommon } from '../common';
export const stripeRetrieveInvoice = createAction({
name: 'retrieve_invoice',
auth: stripeAuth,
displayName: 'Retrieve an Invoice',
description: 'Retrieves the details of an existing invoice by its ID.',
props: {
invoice_id: stripeCommon.invoice,
},
async run(context) {
const { invoice_id } = context.propsValue;
const response = await httpClient.sendRequest({
method: HttpMethod.GET,
url: `${stripeCommon.baseUrl}/invoices/${invoice_id}`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.secret_text,
},
});
return response.body;
},
});

View File

@@ -0,0 +1,33 @@
import { createAction } from '@activepieces/pieces-framework';
import {
httpClient,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { stripeAuth } from '../..';
import { stripeCommon } from '../common';
export const stripeRetrievePaymentIntent = createAction({
name: 'retrieve_payment_intent',
auth: stripeAuth,
displayName: 'Find Payment (by Payment Intent ID)',
description:
'Retrieves the details of a payment by its unique Payment Intent ID.',
props: {
payment_intent_id: stripeCommon.paymentIntent,
},
async run(context) {
const { payment_intent_id } = context.propsValue;
const response = await httpClient.sendRequest({
method: HttpMethod.GET,
url: `${stripeCommon.baseUrl}/payment_intents/${payment_intent_id}`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.secret_text,
},
});
return response.body;
},
});

View File

@@ -0,0 +1,32 @@
import { createAction } from '@activepieces/pieces-framework';
import {
httpClient,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { stripeAuth } from '../..';
import { stripeCommon } from '../common';
export const stripeRetrievePayout = createAction({
name: 'retrieve_payout',
auth: stripeAuth,
displayName: 'Retrieve a Payout',
description: 'Retrieves the details of an existing payout by its ID.',
props: {
payout_id: stripeCommon.payout,
},
async run(context) {
const { payout_id } = context.propsValue;
const response = await httpClient.sendRequest({
method: HttpMethod.GET,
url: `${stripeCommon.baseUrl}/payouts/${payout_id}`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.secret_text,
},
});
return response.body;
},
});

View File

@@ -0,0 +1,34 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
import { stripeAuth } from '../..';
export const stripeSearchCustomer = createAction({
name: 'search_customer',
auth: stripeAuth,
displayName: 'Search Customer',
description: 'Search for a customer in stripe by email',
props: {
email: Property.ShortText({
displayName: 'Email',
description: undefined,
required: true,
}),
},
async run(context) {
const customer = {
email: context.propsValue.email,
};
const response = await httpClient.sendRequest({
method: HttpMethod.GET,
url: 'https://api.stripe.com/v1/customers/search',
headers: {
Authorization: 'Bearer ' + context.auth.secret_text,
'Content-Type': 'application/x-www-form-urlencoded',
},
body: {
query: 'email:' + "'" + customer.email + "'",
},
});
return response.body;
},
});

View File

@@ -0,0 +1,230 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
import Stripe from 'stripe';
import { stripeAuth } from '../..';
import { stripeCommon } from '../common';
interface AugmentedSubscriptionOutput extends Stripe.Subscription {
customer: string | Stripe.Customer | Stripe.DeletedCustomer;
}
const statusOptions = [
{ label: 'All Statuses', value: '' },
{ label: 'Active', value: 'active' },
{ label: 'Past Due', value: 'past_due' },
{ label: 'Unpaid', value: 'unpaid' },
{ label: 'Canceled', value: 'canceled' },
{ label: 'Incomplete', value: 'incomplete' },
{ label: 'Incomplete Expired', value: 'incomplete_expired' },
{ label: 'Trialing', value: 'trialing' },
{ label: 'Paused', value: 'paused' },
]
export const stripeSearchSubscriptions = createAction({
name: 'search_subscriptions',
auth: stripeAuth,
displayName: 'Search Subscriptions',
description: 'Search for subscriptions by price ID, status, customer ID and other filters, including customer details',
props: {
price_ids: Property.LongText({
displayName: 'Price IDs',
description: 'Comma-separated list of price IDs to filter by (e.g., price_1ABC123, price_2DEF456)',
required: false,
}),
status: Property.StaticDropdown({
displayName: 'Subscription Status',
description: 'Filter by subscription status',
required: false,
options: {
options: statusOptions
},
}),
customer_id: Property.ShortText({
displayName: 'Customer ID',
description: 'Filter by specific customer ID (optional)',
required: false,
}),
created_after: Property.DateTime({
displayName: 'Created After',
description: 'Filter subscriptions created after this date (YYYY-MM-DD format)',
required: false,
}),
created_before: Property.DateTime({
displayName: 'Created Before',
description: 'Filter subscriptions created before this date (YYYY-MM-DD format)',
required: false,
}),
limit: Property.Number({
displayName: 'Limit',
description: 'Maximum number of subscriptions to return (default: 100, set to 0 for all)',
required: false,
defaultValue: 100,
}),
fetch_all: Property.Checkbox({
displayName: 'Fetch All Results',
description: 'Fetch all matching subscriptions (ignores limit, may take longer for large datasets)',
required: false,
defaultValue: false,
}),
include_customer_details: Property.Checkbox({
displayName: 'Include Customer Details',
description: 'Fetch detailed customer information for each subscription',
required: false,
defaultValue: true,
}),
},
async run(context) {
const {
price_ids,
status,
customer_id,
created_after,
created_before,
limit = 100,
fetch_all = false,
include_customer_details = true,
} = context.propsValue;
const buildQueryParams = (startingAfter?: string): URLSearchParams => {
const queryParams = new URLSearchParams();
queryParams.append('limit', '100');
queryParams.append('expand[]', 'data.items.data.price');
if (include_customer_details) {
queryParams.append('expand[]', 'data.customer');
}
if (status) {
queryParams.append('status', status);
}
if (customer_id) {
queryParams.append('customer', customer_id);
}
if (created_after) {
const afterTimestamp = Math.floor(new Date(created_after).getTime() / 1000);
queryParams.append('created[gte]', afterTimestamp.toString());
}
if (created_before) {
const beforeTimestamp = Math.floor(new Date(created_before).getTime() / 1000);
queryParams.append('created[lte]', beforeTimestamp.toString());
}
if (startingAfter) {
queryParams.append('starting_after', startingAfter);
}
return queryParams;
};
let allSubscriptions: Stripe.Subscription[] = [];
let hasMore = true;
let startingAfter: string | undefined;
let requestCount = 0;
const maxRequests = fetch_all ? 50 : Math.ceil(limit / 100);
while (hasMore && requestCount < maxRequests) {
const queryParams = buildQueryParams(startingAfter);
const subscriptionsResponse = await httpClient.sendRequest({
method: HttpMethod.GET,
url: `${stripeCommon.baseUrl}/subscriptions?${queryParams.toString()}`,
headers: {
Authorization: 'Bearer ' + context.auth.secret_text,
'Content-Type': 'application/x-www-form-urlencoded',
},
});
if (!subscriptionsResponse.body) {
throw new Error('Failed to fetch subscriptions from Stripe');
}
const subscriptions = subscriptionsResponse.body.data as Stripe.Subscription[];
allSubscriptions = allSubscriptions.concat(subscriptions);
hasMore = subscriptionsResponse.body.has_more;
requestCount++;
if (hasMore && subscriptions.length > 0) {
startingAfter = subscriptions[subscriptions.length - 1].id;
}
if (!fetch_all && allSubscriptions.length >= limit) {
allSubscriptions = allSubscriptions.slice(0, limit);
break;
}
}
let filteredSubscriptions = allSubscriptions;
if (price_ids && price_ids.trim()) {
const priceIdArray = price_ids
.split(',')
.map(id => id.trim())
.filter(id => id.length > 0);
if (priceIdArray.length > 0) {
filteredSubscriptions = filteredSubscriptions.filter((subscription: Stripe.Subscription) => {
return subscription.items.data.some((item: Stripe.SubscriptionItem) =>
item.price && typeof item.price === 'object' && priceIdArray.includes(item.price.id)
);
});
}
}
const finalSubscriptions: AugmentedSubscriptionOutput[] = await Promise.all(
filteredSubscriptions.map(async (subscription: Stripe.Subscription) => {
const resultSubscription: AugmentedSubscriptionOutput = {
...subscription,
};
if (include_customer_details) {
if (typeof subscription.customer === 'object' && subscription.customer !== null && !('deleted' in subscription.customer && subscription.customer.deleted)) {
resultSubscription.customer = subscription.customer as Stripe.Customer;
} else if (typeof subscription.customer === 'string') {
try {
const customerResponse = await httpClient.sendRequest({
method: HttpMethod.GET,
url: `${stripeCommon.baseUrl}/customers/${subscription.customer}`,
headers: {
Authorization: 'Bearer ' + context.auth.secret_text,
},
});
resultSubscription.customer = customerResponse.body as Stripe.Customer
} catch (error) {
console.warn(`Failed to fetch customer details for ${subscription.customer}:`, error);
}
}
}
return resultSubscription;
})
);
return {
success: true,
count: finalSubscriptions.length,
total_fetched: allSubscriptions.length,
requests_made: requestCount,
has_more_available: hasMore && !fetch_all,
pagination_info: {
limit_requested: fetch_all ? 'All' : limit,
fetch_all_enabled: fetch_all,
max_requests_limit: maxRequests,
},
subscriptions: finalSubscriptions,
filters_applied: {
price_ids: price_ids ? price_ids.split(',').map(id => id.trim()).filter(id => id.length > 0) : null,
status: status || null,
customer_id: customer_id || null,
created_after: created_after || null,
created_before: created_before || null,
limit: limit,
fetch_all: fetch_all,
include_customer_details: include_customer_details,
},
};
},
});

View File

@@ -0,0 +1,92 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import {
httpClient,
HttpMethod,
AuthenticationType,
} from '@activepieces/pieces-common';
import { stripeAuth } from '../..';
import { stripeCommon } from '../common';
export const stripeUpdateCustomer = createAction({
name: 'update_customer',
auth: stripeAuth,
displayName: 'Update Customer',
description: 'Modify an existing customers details.',
props: {
customer: stripeCommon.customer,
email: Property.ShortText({
displayName: 'Email',
required: false,
}),
name: Property.ShortText({
displayName: 'Name',
required: false,
}),
description: Property.LongText({
displayName: 'Description',
required: false,
}),
phone: Property.ShortText({
displayName: 'Phone',
required: false,
}),
line1: Property.ShortText({
displayName: 'Address Line 1',
required: false,
}),
postal_code: Property.ShortText({
displayName: 'Postal Code',
required: false,
}),
city: Property.ShortText({
displayName: 'City',
required: false,
}),
state: Property.ShortText({
displayName: 'State',
required: false,
}),
country: Property.ShortText({
displayName: 'Country',
required: false,
}),
},
async run(context) {
const { customer, ...propsValue } = context.propsValue;
const body: { [key: string]: unknown } = {};
if (propsValue.name) body.name = propsValue.name;
if (propsValue.email) body.email = propsValue.email;
if (propsValue.description) body.description = propsValue.description;
if (propsValue.phone) body.phone = propsValue.phone;
const address: { [key: string]: string } = {};
if (propsValue.line1) address.line1 = propsValue.line1;
if (propsValue.city) address.city = propsValue.city;
if (propsValue.state) address.state = propsValue.state;
if (propsValue.postal_code) address.postal_code = propsValue.postal_code;
if (propsValue.country) address.country = propsValue.country;
if (Object.keys(address).length > 0) {
body.address = address;
}
const response = await httpClient.sendRequest({
method: HttpMethod.POST,
url: `https://api.stripe.com/v1/customers/${customer}`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: context.auth.secret_text,
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: body,
});
return response.body;
},
});

View File

@@ -0,0 +1,487 @@
import {
HttpRequest,
HttpMethod,
AuthenticationType,
httpClient,
} from '@activepieces/pieces-common';
import {
Property,
DropdownState,
PiecePropValueSchema
} from '@activepieces/pieces-framework';
import { Stripe } from 'stripe';
import { stripeAuth } from '../..';
import {
StripeCustomerSearchResult,
StripePaymentIntentSearchResult,
StripePayout,
StripeProductSearchResult,
} from './types';
const baseUrl = 'https://api.stripe.com/v1';
export const getClient = (apiKey: string): Stripe => {
return new Stripe(apiKey, {
apiVersion: '2025-05-28.basil',
});
};
export const stripeCommon = {
baseUrl: baseUrl,
subscribeWebhook: async (
eventName: string,
webhookUrl: string,
apiKey: string
): Promise<{ id: string }> => {
const request: HttpRequest = {
method: HttpMethod.POST,
url: `${baseUrl}/webhook_endpoints`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: {
enabled_events: [eventName],
url: webhookUrl,
},
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: apiKey,
},
queryParams: {},
};
const { body: webhook } = await httpClient.sendRequest<{ id: string }>(
request
);
return webhook;
},
unsubscribeWebhook: async (webhookId: string, apiKey: string) => {
const request: HttpRequest = {
method: HttpMethod.DELETE,
url: `${baseUrl}/webhook_endpoints/${webhookId}`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: apiKey,
},
};
return await httpClient.sendRequest(request);
},
invoice: Property.Dropdown({
auth: stripeAuth,
displayName: 'Invoice',
required: true,
refreshers: [],
options: async ({ auth }): Promise<DropdownState<string>> => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your Stripe account first',
};
}
try {
const client = getClient(
auth.secret_text
);
const invoices = await client.invoices.list({
limit: 100,
expand: ['data.customer'],
});
return {
disabled: false,
options: invoices.data
.map((invoice) => {
const customer = invoice.customer as Stripe.Customer | null;
const customerName =
customer?.name || customer?.email || 'Unknown Customer';
const amount = (invoice.total / 100).toFixed(2);
const label = `Invoice #${
invoice.number || invoice.id
} for ${customerName} (${amount} ${invoice.currency.toUpperCase()})`;
return {
value: invoice.id,
label: label,
};
})
.filter(
(option): option is { value: string; label: string } =>
option.value !== undefined && option.value !== null
),
};
} catch (error) {
console.error('Failed to load Stripe invoices:', error);
return {
disabled: true,
options: [],
placeholder: 'Error loading invoices. Check connection.',
};
}
},
}),
customer: Property.Dropdown({
auth: stripeAuth,
displayName: 'Customer',
required: true,
refreshers: [],
options: async ({ auth, searchValue }): Promise<DropdownState<string>> => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your account',
};
}
const request: HttpRequest = {
method: HttpMethod.GET,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: auth.secret_text,
},
url: searchValue
? `${baseUrl}/customers/search`
: `${baseUrl}/customers`,
queryParams: searchValue
? { query: `name~"${searchValue}" OR email~"${searchValue}"` }
: { limit: '100' },
};
const response = await httpClient.sendRequest<{
data: StripeCustomerSearchResult[];
}>(request);
if (response.status === 200) {
return {
disabled: false,
options: response.body.data.map(
(customer: StripeCustomerSearchResult) => ({
value: customer.id,
label: `${customer.name || customer.id} (${
customer.email || 'No Email'
})`,
})
),
};
}
return {
disabled: true,
options: [],
placeholder: "Couldn't load customers",
};
},
}),
product: Property.Dropdown({
auth: stripeAuth,
displayName: 'Product',
required: true,
refreshers: [],
options: async ({ auth, searchValue }): Promise<DropdownState<string>> => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your account',
};
}
let query = "active:'true'";
if (searchValue) {
query += ` AND name~"${searchValue}"`;
}
const response = await httpClient.sendRequest<{
data: StripeProductSearchResult[];
}>({
method: HttpMethod.GET,
url: `${baseUrl}/products/search`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: auth.secret_text,
},
queryParams: {
query: query,
},
});
if (response.status === 200) {
return {
disabled: false,
options: response.body.data.map(
(prod: StripeProductSearchResult) => ({
value: prod.id,
label: prod.name,
})
),
};
}
return {
disabled: true,
options: [],
placeholder: "Couldn't load products",
};
},
}),
price: Property.Dropdown({
auth: stripeAuth,
displayName: 'Price',
required: true,
refreshers: [],
options: async ({ auth, searchValue }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your account',
};
}
const response = await httpClient.sendRequest<{
data: {
id: string;
nickname: string | null;
product: { name: string };
}[];
}>({
method: HttpMethod.GET,
url: `${baseUrl}/prices/search`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: auth.secret_text,
},
queryParams: {
query: `active:'true' AND type:'recurring' AND (product.name~"${
searchValue || ''
}" OR nickname~"${searchValue || ''}")`,
expand: 'data.product',
},
});
if (response.status === 200) {
return {
disabled: false,
options: response.body.data.map((price) => {
const label = price.nickname
? `${price.nickname} (${price.product.name})`
: price.product.name;
return {
value: price.id,
label: label,
};
}),
};
}
return {
disabled: true,
options: [],
placeholder: "Couldn't load prices",
};
},
}),
subscription: Property.Dropdown({
auth: stripeAuth,
displayName: 'Subscription',
required: true,
refreshers: [],
options: async ({ auth }): Promise<DropdownState<string>> => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your account first',
};
}
try {
const client = getClient(
auth.secret_text
);
const subscriptions = await client.subscriptions.list({
limit: 100,
status: 'active',
expand: ['data.customer'],
});
return {
disabled: false,
options: subscriptions.data.map((sub) => {
const customer = sub.customer as Stripe.Customer | null;
const customerInfo =
customer?.name || customer?.email || 'Unknown Customer';
const label = `Subscription for ${customerInfo} (${sub.id})`;
return {
value: sub.id,
label: label,
};
}),
};
} catch (error) {
console.error('Failed to load Stripe subscriptions:', error);
return {
disabled: true,
options: [],
placeholder: "Couldn't load subscriptions. See console.",
};
}
},
}),
payout: Property.Dropdown({
auth: stripeAuth,
displayName: 'Payout',
required: true,
refreshers: [],
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your account',
};
}
const response = await httpClient.sendRequest<{ data: StripePayout[] }>({
method: HttpMethod.GET,
url: `${baseUrl}/payouts`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: auth.secret_text,
},
queryParams: {
limit: '100',
},
});
if (response.status === 200) {
return {
disabled: false,
options: response.body.data.map((payout) => {
const arrivalDate = new Date(
payout.arrival_date * 1000
).toLocaleDateString();
const amount = (payout.amount / 100).toFixed(2);
const label = `Payout on ${arrivalDate} - ${amount} ${payout.currency.toUpperCase()} (${
payout.status
})`;
return {
value: payout.id,
label: label,
};
}),
};
}
return {
disabled: true,
options: [],
placeholder: "Couldn't load payouts",
};
},
}),
paymentIntent: Property.Dropdown({
auth: stripeAuth,
displayName: 'Payment Intent',
required: true,
refreshers: [],
options: async ({ auth, searchValue }): Promise<DropdownState<string>> => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your account',
};
}
let query = "status:'succeeded'";
if (searchValue) {
query += ` AND customer.email~"${searchValue}"`;
}
const response = await httpClient.sendRequest<{
data: StripePaymentIntentSearchResult[];
}>({
method: HttpMethod.GET,
url: `${baseUrl}/payment_intents/search`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: auth.secret_text,
},
queryParams: {
query: query,
'expand[]': 'data.customer',
},
});
if (response.status === 200) {
return {
disabled: false,
options: response.body.data.map(
(intent: StripePaymentIntentSearchResult) => {
const customerInfo =
intent.customer?.name ||
intent.customer?.email ||
'Unknown Customer';
const amount = (intent.amount / 100).toFixed(2);
const label = `Payment from ${customerInfo} - ${amount} ${intent.currency.toUpperCase()}`;
return {
value: intent.id,
label: label,
};
}
),
};
}
return {
disabled: true,
options: [],
placeholder: "Couldn't load payment intents",
};
},
}),
paymentLink: Property.Dropdown({
auth: stripeAuth,
displayName: 'Payment Link',
required: true,
refreshers: [],
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your account',
};
}
try {
const client = getClient(auth.secret_text);
const paymentLinks = await client.paymentLinks.list({
active: true,
limit: 100,
});
return {
disabled: false,
options: paymentLinks.data.map((link) => ({
value: link.id,
label: link.url,
})),
};
} catch (e) {
return {
disabled: true,
options: [],
placeholder: "Couldn't load payment links",
};
}
},
}),
};

View File

@@ -0,0 +1,60 @@
export interface StripeWebhookInformation {
webhookId: string;
}
export interface StripeCustomerSearchResult {
id: string;
name: string | null;
email: string | null;
}
export interface StripeProductSearchResult {
id: string;
name: string;
}
export interface StripePriceSearchResult {
id: string;
nickname: string | null;
product: {
name: string;
};
}
export interface StripeSubscriptionSearchResult {
id: string;
customer: {
name: string;
email: string;
};
}
export interface StripeInvoiceSearchResult {
id: string;
number: string | null;
customer: { name: string | null; email: string | null } | null;
total: number;
currency: string;
}
export interface StripePaymentIntentSearchResult {
id: string;
amount: number;
currency: string;
customer: { name: string | null; email: string | null } | null;
description: string | null;
}
export interface StripePayout {
id: string;
amount: number;
currency: string;
arrival_date: number;
status: string;
destination: string;
}
export interface StripePaymentLink {
id: string;
url: string;
}

View File

@@ -0,0 +1,134 @@
import {
createTrigger,
TriggerStrategy,
Property,
} from '@activepieces/pieces-framework';
import { stripeCommon } from '../common';
import { StripeWebhookInformation } from '../common/types';
import { stripeAuth } from '../..';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
import { isEmpty } from '@activepieces/shared';
type StripeWebhookPayload = {
data: {
object: {
[key: string]: unknown;
};
};
};
export const stripeCanceledSubscription = createTrigger({
auth: stripeAuth,
name: 'canceled_subscription',
displayName: 'Canceled Subscription',
description: 'Fires when a subscription is canceled.',
props: {
customer: Property.ShortText({
displayName: 'Customer ID',
description:
'Only trigger for subscriptions belonging to this customer ID (e.g., `cus_...`).',
required: false,
}),
},
sampleData: {
id: 'sub_1MlPf9LkdIwHu7ixB6VIYRyX',
object: 'subscription',
application: null,
application_fee_percent: null,
billing_cycle_anchor: 1678768838,
cancel_at_period_end: false,
canceled_at: 1678768842,
cancellation_details: {
comment: 'User requested cancellation via support.',
feedback: 'missing_features',
reason: 'cancellation_requested',
},
collection_method: 'charge_automatically',
created: 1678768838,
currency: 'usd',
customer: 'cus_NWSaVkvdacCUi4',
ended_at: 1678768842,
items: {
object: 'list',
data: [
{
id: 'si_NWSaWTp80M123q',
object: 'subscription_item',
price: {
id: 'price_1MlPf7LkdIwHu7ixgcbP7cwE',
object: 'price',
active: true,
currency: 'usd',
product: 'prod_NWSaMgipulx8IQ',
type: 'recurring',
unit_amount: 1099,
},
quantity: 1,
},
],
},
latest_invoice: 'in_1MlPf9LkdIwHu7ixEo6hdgCw',
livemode: false,
metadata: {},
start_date: 1678768838,
status: 'canceled',
},
type: TriggerStrategy.WEBHOOK,
async onEnable(context) {
const webhook = await stripeCommon.subscribeWebhook(
'customer.subscription.deleted',
context.webhookUrl,
context.auth.secret_text
);
await context.store.put<StripeWebhookInformation>(
'_canceled_subscription_trigger',
{
webhookId: webhook.id,
}
);
},
async onDisable(context) {
const webhookInfo = await context.store.get<StripeWebhookInformation>(
'_canceled_subscription_trigger'
);
if (webhookInfo !== null && webhookInfo !== undefined) {
await stripeCommon.unsubscribeWebhook(
webhookInfo.webhookId,
context.auth.secret_text
);
}
},
async test(context) {
const response = await httpClient.sendRequest<{ data: { id: string }[] }>({
method: HttpMethod.GET,
url: 'https://api.stripe.com/v1/subscriptions',
headers: {
Authorization: 'Bearer ' + context.auth.secret_text,
'Content-Type': 'application/x-www-form-urlencoded',
},
queryParams: {
status: 'canceled',
limit: '5',
},
});
if (isEmpty(response.body) || isEmpty(response.body.data)) return [];
return response.body.data;
},
async run(context) {
const payloadBody = context.payload.body as StripeWebhookPayload;
const subscriptionObject = payloadBody.data.object;
const { customer } = context.propsValue;
if (customer && subscriptionObject['customer'] !== customer) {
return [];
}
return [subscriptionObject];
},
});

View File

@@ -0,0 +1,110 @@
import {
createTrigger,
TriggerStrategy,
Property,
} from '@activepieces/pieces-framework';
import { stripeCommon } from '../common';
import { StripeWebhookInformation } from '../common/types';
import { stripeAuth } from '../..';
import { isEmpty } from '@activepieces/shared';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
type StripeWebhookPayload = {
data: {
object: {
[key: string]: unknown;
};
};
};
export const stripeCheckoutSessionCompleted = createTrigger({
auth: stripeAuth,
name: 'checkout_session_completed',
displayName: 'Checkout Session Completed',
description:
'Fires when a Stripe Checkout Session is successfully completed.',
props: {
customer: Property.ShortText({
displayName: 'Customer ID',
description:
'Only trigger for checkout sessions created by this customer ID (e.g., `cus_...`).',
required: false,
}),
},
sampleData: {
id: 'cs_test_a11YYufWQzNY63zpQ6QSNRQhkUpVph4WRmzW0zWJO2znZKdVujZ0N0S22u',
object: 'checkout.session',
amount_subtotal: 2198,
amount_total: 2198,
created: 1679600215,
currency: 'usd',
customer: 'cus_NWSaVkvdacCUi4',
customer_details: {
email: 'jenny.rosen@example.com',
name: 'Jenny Rosen',
phone: null,
},
livemode: false,
mode: 'payment',
payment_intent: 'pi_12345ABC',
payment_status: 'paid',
status: 'complete',
success_url: 'https://example.com/success',
url: null,
},
type: TriggerStrategy.WEBHOOK,
async onEnable(context) {
const webhook = await stripeCommon.subscribeWebhook(
'checkout.session.completed',
context.webhookUrl,
context.auth.secret_text
);
await context.store.put<StripeWebhookInformation>(
'_checkout_session_completed_trigger',
{
webhookId: webhook.id,
}
);
},
async onDisable(context) {
const webhookInfo = await context.store.get<StripeWebhookInformation>(
'_checkout_session_completed_trigger'
);
if (webhookInfo !== null && webhookInfo !== undefined) {
await stripeCommon.unsubscribeWebhook(
webhookInfo.webhookId,
context.auth.secret_text
);
}
},
async test(context) {
const response = await httpClient.sendRequest<{ data: { id: string }[] }>({
method: HttpMethod.GET,
url: 'https://api.stripe.com/v1/checkout/sessions',
headers: {
Authorization: 'Bearer ' + context.auth.secret_text,
'Content-Type': 'application/x-www-form-urlencoded',
},
queryParams: {
status: 'complete',
limit: '5',
},
});
if (isEmpty(response.body) || isEmpty(response.body.data)) return [];
return response.body.data;
},
async run(context) {
const payloadBody = context.payload.body as StripeWebhookPayload;
const sessionObject = payloadBody.data.object;
const { customer } = context.propsValue;
if (customer && sessionObject['customer'] !== customer) {
return [];
}
return [sessionObject];
},
});

View File

@@ -0,0 +1,121 @@
import {
createTrigger,
TriggerStrategy,
Property,
} from '@activepieces/pieces-framework';
import { stripeCommon } from '../common';
import { StripeWebhookInformation } from '../common/types';
import { stripeAuth } from '../..';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
import { isEmpty } from '@activepieces/shared';
type StripeWebhookPayload = {
data: {
object: {
[key: string]: unknown;
};
};
};
export const stripeInvoicePaymentFailed = createTrigger({
auth: stripeAuth,
name: 'invoice_payment_failed',
displayName: 'Invoice Payment Failed',
description: 'Fires when a payment against an invoice fails.',
props: {
customer: Property.ShortText({
displayName: 'Customer ID',
description:
'Only trigger for invoices belonging to this customer ID (e.g., `cus_...`).',
required: false,
}),
},
sampleData: {
id: 'in_1MtHbELkdIwHu7ixl4OzzPMv',
object: 'invoice',
customer: 'cus_NeZwdNtLEOXuvB',
status: 'open',
amount_due: 1500,
amount_paid: 0,
amount_remaining: 1500,
attempt_count: 1,
attempted: true,
billing_reason: 'subscription_cycle',
collection_method: 'charge_automatically',
created: 1680644467,
currency: 'usd',
customer_email: 'jennyrosen@example.com',
customer_name: 'Jenny Rosen',
hosted_invoice_url: 'https://invoice.stripe.com/i/acct_...',
last_finalization_error: {
code: 'card_declined',
doc_url: 'https://stripe.com/docs/error-codes/card-declined',
message: 'Your card was declined.',
param: '',
payment_intent: {
id: 'pi_123',
object: 'payment_intent',
},
type: 'card_error',
},
livemode: false,
next_payment_attempt: 1681251667,
},
type: TriggerStrategy.WEBHOOK,
async onEnable(context) {
const webhook = await stripeCommon.subscribeWebhook(
'invoice.payment_failed',
context.webhookUrl,
context.auth.secret_text
);
await context.store.put<StripeWebhookInformation>(
'_invoice_payment_failed_trigger',
{
webhookId: webhook.id,
}
);
},
async onDisable(context) {
const webhookInfo = await context.store.get<StripeWebhookInformation>(
'_invoice_payment_failed_trigger'
);
if (webhookInfo !== null && webhookInfo !== undefined) {
await stripeCommon.unsubscribeWebhook(
webhookInfo.webhookId,
context.auth.secret_text
);
}
},
async test(context) {
const response = await httpClient.sendRequest<{ data: { id: string }[] }>({
method: HttpMethod.GET,
url: 'https://api.stripe.com/v1/checkout/invoices',
headers: {
Authorization: 'Bearer ' + context.auth.secret_text,
'Content-Type': 'application/x-www-form-urlencoded',
},
queryParams: {
status: 'failed',
limit: '5',
},
});
if (isEmpty(response.body) || isEmpty(response.body.data)) return [];
return response.body.data;
},
async run(context) {
const payloadBody = context.payload.body as StripeWebhookPayload;
const invoiceObject = payloadBody.data.object;
const { customer } = context.propsValue;
if (customer && invoiceObject['customer'] !== customer) {
return [];
}
return [invoiceObject];
},
});

View File

@@ -0,0 +1,101 @@
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
import { stripeCommon } from '../common';
import { StripeWebhookInformation } from '../common/types';
import { stripeAuth } from '../..';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
import { isEmpty } from '@activepieces/shared';
type StripeWebhookPayload = {
data: {
object: unknown;
};
};
export const stripeNewCharge = createTrigger({
auth: stripeAuth,
name: 'new_charge',
displayName: 'New Charge',
description: 'Fires when a charge is successfully completed.',
props: {},
sampleData: {
id: 'ch_3MmlLrLkdIwHu7ix0snN0B15',
object: 'charge',
amount: 1099,
amount_captured: 1099,
amount_refunded: 0,
application: null,
application_fee: null,
application_fee_amount: null,
balance_transaction: 'txn_3MmlLrLkdIwHu7ix0uke3Ezy',
billing_details: {
address: {
city: null,
country: null,
line1: null,
line2: null,
postal_code: null,
state: null,
},
email: 'customer@example.com',
name: 'John Doe',
phone: null,
},
captured: true,
created: 1679090539,
currency: 'usd',
customer: 'cus_ABC123',
description: 'My First Test Charge',
disputed: false,
paid: true,
payment_intent: 'pi_123',
payment_method: 'pm_123',
receipt_url: 'https://pay.stripe.com/receipts/...',
refunded: false,
status: 'succeeded',
},
type: TriggerStrategy.WEBHOOK,
async onEnable(context) {
const webhook = await stripeCommon.subscribeWebhook(
'charge.succeeded',
context.webhookUrl,
context.auth.secret_text
);
await context.store.put<StripeWebhookInformation>('_new_charge_trigger', {
webhookId: webhook.id,
});
},
async onDisable(context) {
const webhookInfo = await context.store.get<StripeWebhookInformation>(
'_new_charge_trigger'
);
if (webhookInfo !== null && webhookInfo !== undefined) {
await stripeCommon.unsubscribeWebhook(
webhookInfo.webhookId,
context.auth.secret_text
);
}
},
async test(context) {
const response = await httpClient.sendRequest<{ data: { id: string }[] }>({
method: HttpMethod.GET,
url: 'https://api.stripe.com/v1/checkout/charges',
headers: {
Authorization: 'Bearer ' + context.auth.secret_text,
'Content-Type': 'application/x-www-form-urlencoded',
},
queryParams: {
limit: '5',
},
});
if (isEmpty(response.body) || isEmpty(response.body.data)) return [];
return response.body.data;
},
async run(context) {
const payloadBody = context.payload.body as StripeWebhookPayload;
return [payloadBody.data.object];
},
});

View File

@@ -0,0 +1,94 @@
import { createTrigger } from '@activepieces/pieces-framework';
import { TriggerStrategy } from '@activepieces/pieces-framework';
import { stripeCommon } from '../common';
import { stripeAuth } from '../..';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
import { isEmpty } from '@activepieces/shared';
export const stripeNewCustomer = createTrigger({
auth: stripeAuth,
name: 'new_customer',
displayName: 'New Customer',
description: 'Triggers when a new customer is created',
props: {},
sampleData: {
id: 'cus_NGtyEf4hNGTj3p',
object: 'customer',
address: null,
balance: 0,
created: 1675180509,
currency: null,
default_currency: null,
default_source: null,
delinquent: false,
description: null,
discount: null,
email: 'jane@example.com',
invoice_prefix: 'B7162248',
invoice_settings: {
custom_fields: null,
default_payment_method: null,
footer: null,
rendering_options: null,
},
livemode: false,
metadata: {},
name: 'John Doe',
next_invoice_sequence: 1,
phone: null,
preferred_locales: [],
shipping: null,
tax_exempt: 'none',
test_clock: null,
},
type: TriggerStrategy.WEBHOOK,
async onEnable(context) {
const webhook = await stripeCommon.subscribeWebhook(
'customer.created',
context.webhookUrl,
context.auth.secret_text
);
await context.store.put<WebhookInformation>('_new_customer_trigger', {
webhookId: webhook.id,
});
},
async onDisable(context) {
const response = await context.store?.get<WebhookInformation>(
'_new_customer_trigger'
);
if (response !== null && response !== undefined) {
await stripeCommon.unsubscribeWebhook(response.webhookId, context.auth.secret_text);
}
},
async test(context) {
const response = await httpClient.sendRequest<{ data: { id: string }[] }>({
method: HttpMethod.GET,
url: 'https://api.stripe.com/v1/checkout/customers',
headers: {
Authorization: 'Bearer ' + context.auth.secret_text,
'Content-Type': 'application/x-www-form-urlencoded',
},
queryParams: {
limit: '5',
},
});
if (isEmpty(response.body) || isEmpty(response.body.data)) return [];
return response.body.data;
},
async run(context) {
const payloadBody = context.payload.body as PayloadBody;
return [payloadBody.data.object];
},
});
type PayloadBody = {
data: {
object: unknown;
};
};
interface WebhookInformation {
webhookId: string;
}

View File

@@ -0,0 +1,117 @@
import {
createTrigger,
TriggerStrategy,
Property,
} from '@activepieces/pieces-framework';
import { stripeCommon } from '../common';
import { StripeWebhookInformation } from '../common/types';
import { stripeAuth } from '../..';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
import { isEmpty } from '@activepieces/shared';
type StripeWebhookPayload = {
data: {
object: {
[key: string]: unknown;
};
};
};
export const stripeNewDispute = createTrigger({
auth: stripeAuth,
name: 'new_dispute',
displayName: 'New Dispute',
description: 'Fires when a customer disputes a charge.',
props: {
charge: Property.ShortText({
displayName: 'Charge ID',
description:
'Only trigger for disputes related to this Charge ID (e.g., `ch_...`).',
required: false,
}),
payment_intent: Property.ShortText({
displayName: 'Payment Intent ID',
description:
'Only trigger for disputes related to this Payment Intent ID (e.g., `pi_...`).',
required: false,
}),
},
sampleData: {
id: 'du_1MtJUT2eZvKYlo2CNaw2HvEv',
object: 'dispute',
amount: 1000,
balance_transactions: [],
charge: 'ch_1AZtxr2eZvKYlo2CJDX8whov',
created: 1680651737,
currency: 'usd',
evidence_details: {
due_by: 1682294399,
has_evidence: false,
past_due: false,
submission_count: 0,
},
is_charge_refundable: true,
livemode: false,
metadata: {},
payment_intent: 'pi_1AZtxr2eZvKYlo2CJDX8whov',
reason: 'general',
status: 'needs_response',
},
type: TriggerStrategy.WEBHOOK,
async onEnable(context) {
const webhook = await stripeCommon.subscribeWebhook(
'charge.dispute.created',
context.webhookUrl,
context.auth.secret_text
);
await context.store.put<StripeWebhookInformation>('_new_dispute_trigger', {
webhookId: webhook.id,
});
},
async onDisable(context) {
const webhookInfo = await context.store.get<StripeWebhookInformation>(
'_new_dispute_trigger'
);
if (webhookInfo !== null && webhookInfo !== undefined) {
await stripeCommon.unsubscribeWebhook(
webhookInfo.webhookId,
context.auth.secret_text
);
}
},
async test(context) {
const response = await httpClient.sendRequest<{ data: { id: string }[] }>({
method: HttpMethod.GET,
url: 'https://api.stripe.com/v1/checkout/disputes',
headers: {
Authorization: 'Bearer ' + context.auth.secret_text,
'Content-Type': 'application/x-www-form-urlencoded',
},
queryParams: {
limit: '5',
},
});
if (isEmpty(response.body) || isEmpty(response.body.data)) return [];
return response.body.data;
},
async run(context) {
const payloadBody = context.payload.body as StripeWebhookPayload;
const disputeObject = payloadBody.data.object;
const { charge, payment_intent } = context.propsValue;
if (charge && disputeObject['charge'] !== charge) {
return [];
}
if (payment_intent && disputeObject['payment_intent'] !== payment_intent) {
return [];
}
return [disputeObject];
},
});

View File

@@ -0,0 +1,132 @@
import {
createTrigger,
TriggerStrategy,
Property,
} from '@activepieces/pieces-framework';
import { stripeCommon } from '../common';
import { StripeWebhookInformation } from '../common/types';
import { stripeAuth } from '../..';
import { isEmpty } from '@activepieces/shared';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
type StripeWebhookPayload = {
data: {
object: {
[key: string]: unknown;
};
};
};
export const stripeNewInvoice = createTrigger({
auth: stripeAuth,
name: 'new_invoice',
displayName: 'New Invoice',
description:
'Fires when an invoice is created. Supports filters like status, customer, subscription.',
props: {
status: Property.StaticDropdown({
displayName: 'Status',
description: 'Only trigger for invoices with this status.',
required: false,
options: {
options: [
{ label: 'Draft', value: 'draft' },
{ label: 'Open', value: 'open' },
{ label: 'Paid', value: 'paid' },
{ label: 'Uncollectible', value: 'uncollectible' },
{ label: 'Void', value: 'void' },
],
},
}),
customer: Property.ShortText({
displayName: 'Customer ID',
description:
'Only trigger for invoices belonging to this customer ID (e.g., `cus_...`).',
required: false,
}),
subscription: Property.ShortText({
displayName: 'Subscription ID',
description:
'Only trigger for invoices belonging to this subscription ID (e.g., `sub_...`).',
required: false,
}),
},
sampleData: {
id: 'in_1MtHbELkdIwHu7ixl4OzzPMv',
object: 'invoice',
customer: 'cus_NeZwdNtLEOXuvB',
subscription: 'sub_12345ABC',
status: 'paid',
amount_due: 999,
amount_paid: 999,
amount_remaining: 0,
billing_reason: 'subscription_cycle',
collection_method: 'charge_automatically',
created: 1680644467,
currency: 'usd',
customer_email: 'jennyrosen@example.com',
customer_name: 'Jenny Rosen',
hosted_invoice_url: 'https://invoice.stripe.com/i/acct_...',
invoice_pdf: 'https://pay.stripe.com/invoice/acct_.../pdf',
livemode: false,
number: 'ABC-123-456',
},
type: TriggerStrategy.WEBHOOK,
async onEnable(context) {
const webhook = await stripeCommon.subscribeWebhook(
'invoice.created',
context.webhookUrl,
context.auth.secret_text
);
await context.store.put<StripeWebhookInformation>('_new_invoice_trigger', {
webhookId: webhook.id,
});
},
async onDisable(context) {
const webhookInfo = await context.store.get<StripeWebhookInformation>(
'_new_invoice_trigger'
);
if (webhookInfo !== null && webhookInfo !== undefined) {
await stripeCommon.unsubscribeWebhook(
webhookInfo.webhookId,
context.auth.secret_text
);
}
},
async test(context) {
const response = await httpClient.sendRequest<{ data: { id: string }[] }>({
method: HttpMethod.GET,
url: 'https://api.stripe.com/v1/invoices',
headers: {
Authorization: 'Bearer ' + context.auth.secret_text,
'Content-Type': 'application/x-www-form-urlencoded',
},
queryParams: {
limit: '5',
},
});
if (isEmpty(response.body) || isEmpty(response.body.data)) return [];
return response.body.data;
},
async run(context) {
const payloadBody = context.payload.body as StripeWebhookPayload;
const invoiceObject = payloadBody.data.object;
const { status, customer, subscription } = context.propsValue;
if (status && invoiceObject['status'] !== status) {
return [];
}
if (customer && invoiceObject['customer'] !== customer) {
return [];
}
if (subscription && invoiceObject['subscription'] !== subscription) {
return [];
}
return [invoiceObject];
},
});

View File

@@ -0,0 +1,87 @@
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
import { stripeCommon } from '../common';
import { StripeWebhookInformation } from '../common/types';
import { stripeAuth } from '../..';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
import { isEmpty } from '@activepieces/shared';
type StripeWebhookPayload = {
data: {
object: unknown;
};
};
export const stripeNewPaymentLink = createTrigger({
auth: stripeAuth,
name: 'new_payment_link',
displayName: 'New Payment Link',
description: 'Fires when a new Payment Link is created.',
props: {},
sampleData: {
id: 'plink_1MoC3ULkdIwHu7ixZjtGpVl2',
object: 'payment_link',
active: true,
after_completion: {
hosted_confirmation: {
custom_message: null,
},
type: 'hosted_confirmation',
},
allow_promotion_codes: false,
currency: 'usd',
customer_creation: 'if_required',
livemode: false,
metadata: {},
payment_method_collection: 'always',
url: 'https://buy.stripe.com/test_cN25nr0iZ7bUa7meUY',
},
type: TriggerStrategy.WEBHOOK,
async onEnable(context) {
const webhook = await stripeCommon.subscribeWebhook(
'payment_link.created',
context.webhookUrl,
context.auth.secret_text
);
await context.store.put<StripeWebhookInformation>(
'_new_payment_link_trigger',
{
webhookId: webhook.id,
}
);
},
async onDisable(context) {
const webhookInfo = await context.store.get<StripeWebhookInformation>(
'_new_payment_link_trigger'
);
if (webhookInfo !== null && webhookInfo !== undefined) {
await stripeCommon.unsubscribeWebhook(
webhookInfo.webhookId,
context.auth.secret_text
);
}
},
async test(context) {
const response = await httpClient.sendRequest<{ data: { id: string }[] }>({
method: HttpMethod.GET,
url: 'https://api.stripe.com/v1/checkout/payment_links',
headers: {
Authorization: 'Bearer ' + context.auth.secret_text,
'Content-Type': 'application/x-www-form-urlencoded',
},
queryParams: {
limit: '5',
},
});
if (isEmpty(response.body) || isEmpty(response.body.data)) return [];
return response.body.data;
},
async run(context) {
const payloadBody = context.payload.body as StripeWebhookPayload;
return [payloadBody.data.object];
},
});

View File

@@ -0,0 +1,161 @@
import { createTrigger } from '@activepieces/pieces-framework';
import { TriggerStrategy } from '@activepieces/pieces-framework';
import { stripeCommon } from '../common';
import { stripeAuth } from '../..';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
import { isEmpty } from '@activepieces/shared';
export const stripeNewPayment = createTrigger({
auth: stripeAuth,
name: 'new_payment',
displayName: 'New Payment',
description: 'Triggers when a new payment is made',
props: {},
type: TriggerStrategy.WEBHOOK,
sampleData: {
id: 'ch_3MWM7aKZ0dZRqLEK1soCKVrq',
object: 'charge',
amount: 10000,
amount_captured: 10000,
amount_refunded: 0,
application: null,
application_fee: null,
application_fee_amount: null,
balance_transaction: 'txn_3MWM7aKZ0dZRqLEK1VyE8QH1',
billing_details: {
address: {
city: null,
country: 'DE',
line1: null,
line2: null,
postal_code: null,
state: null,
},
email: 'test@gmail.com',
name: 'Test user',
phone: null,
},
calculated_statement_descriptor: 'WWW.ACTIVEPIECES.COM',
captured: true,
created: 1675180355,
currency: 'usd',
customer: 'cus_NGtvUQ18FJXcGI',
description: 'Subscription creation',
destination: null,
dispute: null,
disputed: false,
failure_balance_transaction: null,
failure_code: null,
failure_message: null,
fraud_details: {},
invoice: 'in_1MWM7ZKZ0dZRqLEKQbrgSBnh',
livemode: false,
metadata: {},
on_behalf_of: null,
order: null,
outcome: {
network_status: 'approved_by_network',
reason: null,
risk_level: 'normal',
risk_score: 64,
seller_message: 'Payment complete.',
type: 'authorized',
},
paid: true,
payment_intent: 'pi_3MWM7aKZ0dZRqLEK1BsblcVI',
payment_method: 'pm_1MWM8MKZ0dZRqLEKnIH41f76',
payment_method_details: {
card: {
brand: 'visa',
checks: {
address_line1_check: null,
address_postal_code_check: null,
cvc_check: 'pass',
},
country: 'US',
exp_month: 12,
exp_year: 2034,
fingerprint: 't8SMsmS4h2vvODpN',
funding: 'credit',
installments: null,
last4: '4242',
mandate: null,
network: 'visa',
three_d_secure: null,
wallet: null,
},
type: 'card',
},
receipt_email: null,
receipt_number: null,
receipt_url:
'https://pay.stripe.com/receipts/invoices/CAcaFwoVYWNjdF8xS214ZEtLWjBkWlJxTEVLKMXy5J4GMgZcuppYWF06LBZEoiAhZ6H7EoJ3bN-BMHCXdaW-_i-ywhSIG9wPGTmtE0CdpD75s1hIyprK?s=ap',
refunded: false,
refunds: {
object: 'list',
data: [],
has_more: false,
total_count: 0,
url: '/v1/charges/ch_3MWM7aKZ0dZRqLEK1soCKVrq/refunds',
},
review: null,
shipping: null,
source: null,
source_transfer: null,
statement_descriptor: null,
statement_descriptor_suffix: null,
status: 'succeeded',
transfer_data: null,
transfer_group: null,
},
async onEnable(context) {
const webhook = await stripeCommon.subscribeWebhook(
'charge.succeeded',
context.webhookUrl!,
context.auth.secret_text
);
await context.store?.put<WebhookInformation>('_new_payment_trigger', {
webhookId: webhook.id,
});
},
async onDisable(context) {
const response = await context.store?.get<WebhookInformation>(
'_new_payment_trigger'
);
if (response !== null && response !== undefined) {
await stripeCommon.unsubscribeWebhook(response.webhookId, context.auth.secret_text);
}
},
async test(context) {
const response = await httpClient.sendRequest<{ data: { id: string }[] }>({
method: HttpMethod.GET,
url: 'https://api.stripe.com/v1/checkout/payment_intents',
headers: {
Authorization: 'Bearer ' + context.auth.secret_text,
'Content-Type': 'application/x-www-form-urlencoded',
},
queryParams: {
status: 'succeeded',
limit: '5',
},
});
if (isEmpty(response.body) || isEmpty(response.body.data)) return [];
return response.body.data;
},
async run(context) {
const payloadBody = context.payload.body as PayloadBody;
return [payloadBody.data.object];
},
});
type PayloadBody = {
data: {
object: unknown;
};
};
interface WebhookInformation {
webhookId: string;
}

View File

@@ -0,0 +1,109 @@
import {
createTrigger,
TriggerStrategy,
Property,
} from '@activepieces/pieces-framework';
import { stripeCommon } from '../common';
import { StripeWebhookInformation } from '../common/types';
import { stripeAuth } from '../..';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
import { isEmpty } from '@activepieces/shared';
type StripeWebhookPayload = {
data: {
object: {
[key: string]: unknown;
};
};
};
export const stripeNewRefund = createTrigger({
auth: stripeAuth,
name: 'new_refund',
displayName: 'New Refund',
description: 'Fires when a charge is refunded (full or partial).',
props: {
charge: Property.ShortText({
displayName: 'Charge ID',
description:
'Only trigger for refunds related to this Charge ID (e.g., `ch_...`).',
required: false,
}),
payment_intent: Property.ShortText({
displayName: 'Payment Intent ID',
description:
'Only trigger for refunds related to this Payment Intent ID (e.g., `pi_...`).',
required: false,
}),
},
sampleData: {
id: 're_1Nispe2eZvKYlo2Cd31jOCgZ',
object: 'refund',
amount: 1000,
balance_transaction: 'txn_1Nispe2eZvKYlo2CYezqFhEx',
charge: 'ch_1NirD82eZvKYlo2CIvbtLWuY',
created: 1692942318,
currency: 'usd',
metadata: {},
payment_intent: 'pi_1GszsK2eZvKYlo2CfhZyoZLp',
reason: 'requested_by_customer',
receipt_number: null,
status: 'succeeded',
},
type: TriggerStrategy.WEBHOOK,
async onEnable(context) {
const webhook = await stripeCommon.subscribeWebhook(
'refund.created',
context.webhookUrl,
context.auth.secret_text
);
await context.store.put<StripeWebhookInformation>('_new_refund_trigger', {
webhookId: webhook.id,
});
},
async onDisable(context) {
const webhookInfo = await context.store.get<StripeWebhookInformation>(
'_new_refund_trigger'
);
if (webhookInfo !== null && webhookInfo !== undefined) {
await stripeCommon.unsubscribeWebhook(
webhookInfo.webhookId,
context.auth.secret_text
);
}
},
async test(context) {
const response = await httpClient.sendRequest<{ data: { id: string }[] }>({
method: HttpMethod.GET,
url: 'https://api.stripe.com/v1/checkout/refunds',
headers: {
Authorization: 'Bearer ' + context.auth.secret_text,
'Content-Type': 'application/x-www-form-urlencoded',
},
queryParams: {
limit: '5',
},
});
if (isEmpty(response.body) || isEmpty(response.body.data)) return [];
return response.body.data;
},
async run(context) {
const payloadBody = context.payload.body as StripeWebhookPayload;
const refundObject = payloadBody.data.object;
const { charge, payment_intent } = context.propsValue;
if (charge && refundObject['charge'] !== charge) {
return [];
}
if (payment_intent && refundObject['payment_intent'] !== payment_intent) {
return [];
}
return [refundObject];
},
});

View File

@@ -0,0 +1,203 @@
import { createTrigger } from '@activepieces/pieces-framework';
import { TriggerStrategy } from '@activepieces/pieces-framework';
import { stripeCommon } from '../common';
import { stripeAuth } from '../..';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
import { isEmpty } from '@activepieces/shared';
export const stripeNewSubscription = createTrigger({
auth: stripeAuth,
name: 'new_subscription',
displayName: 'New Subscription',
description: 'Triggers when a new subscription is made',
props: {},
type: TriggerStrategy.WEBHOOK,
sampleData: {
id: 'sub_1MWMJXKZ0dZRqLEKJX80JXfv',
object: 'subscription',
application: null,
application_fee_percent: null,
automatic_tax: {
enabled: true,
},
billing_cycle_anchor: 1675181047,
billing_thresholds: null,
cancel_at: null,
cancel_at_period_end: false,
canceled_at: null,
collection_method: 'charge_automatically',
created: 1675181047,
currency: 'usd',
current_period_end: 1677600247,
current_period_start: 1675181047,
customer: 'cus_NGtvUQ18FJXcGI',
days_until_due: null,
default_payment_method: 'pm_1MWM8MKZ0dZRqLEKnIH41f76',
default_source: null,
default_tax_rates: [],
description: null,
discount: null,
ended_at: null,
items: {
object: 'list',
data: [
{
id: 'si_NGu7pb7hS3Rps3',
object: 'subscription_item',
billing_thresholds: null,
created: 1675181048,
metadata: {},
plan: {
id: 'price_1MWLz3KZ0dZRqLEK06gRMHCF',
object: 'plan',
active: true,
aggregate_usage: null,
amount: 10000,
amount_decimal: '10000',
billing_scheme: 'per_unit',
created: 1675179777,
currency: 'usd',
interval: 'month',
interval_count: 1,
livemode: false,
metadata: {},
nickname: null,
product: 'prod_NGtm3AlvaGjaLN',
tiers_mode: null,
transform_usage: null,
trial_period_days: null,
usage_type: 'licensed',
},
price: {
id: 'price_1MWLz3KZ0dZRqLEK06gRMHCF',
object: 'price',
active: true,
billing_scheme: 'per_unit',
created: 1675179777,
currency: 'usd',
custom_unit_amount: null,
livemode: false,
lookup_key: null,
metadata: {},
nickname: null,
product: 'prod_NGtm3AlvaGjaLN',
recurring: {
aggregate_usage: null,
interval: 'month',
interval_count: 1,
trial_period_days: null,
usage_type: 'licensed',
},
tax_behavior: 'exclusive',
tiers_mode: null,
transform_quantity: null,
type: 'recurring',
unit_amount: 10000,
unit_amount_decimal: '10000',
},
quantity: 1,
subscription: 'sub_1MWMJXKZ0dZRqLEKJX80JXfv',
tax_rates: [],
},
],
has_more: false,
total_count: 1,
url: '/v1/subscription_items?subscription=sub_1MWMJXKZ0dZRqLEKJX80JXfv',
},
latest_invoice: 'in_1MWMJXKZ0dZRqLEKIu4a51u7',
livemode: false,
metadata: {},
next_pending_invoice_item_invoice: null,
on_behalf_of: null,
pause_collection: null,
payment_settings: {
payment_method_options: null,
payment_method_types: null,
save_default_payment_method: 'off',
},
pending_invoice_item_interval: null,
pending_setup_intent: null,
pending_update: null,
plan: {
id: 'price_1MWLz3KZ0dZRqLEK06gRMHCF',
object: 'plan',
active: true,
aggregate_usage: null,
amount: 10000,
amount_decimal: '10000',
billing_scheme: 'per_unit',
created: 1675179777,
currency: 'usd',
interval: 'month',
interval_count: 1,
livemode: false,
metadata: {},
nickname: null,
product: 'prod_NGtm3AlvaGjaLN',
tiers_mode: null,
transform_usage: null,
trial_period_days: null,
usage_type: 'licensed',
},
quantity: 1,
schedule: null,
start_date: 1675181047,
status: 'active',
test_clock: null,
transfer_data: null,
trial_end: null,
trial_start: null,
},
async onEnable(context) {
const webhook = await stripeCommon.subscribeWebhook(
'customer.subscription.created',
context.webhookUrl!,
context.auth.secret_text
);
await context.store?.put<WebhookInformation>(
'_new_customer_subscription_trigger',
{
webhookId: webhook.id,
}
);
},
async onDisable(context) {
const response = await context.store?.get<WebhookInformation>(
'_new_customer_subscription_trigger'
);
if (response !== null && response !== undefined) {
await stripeCommon.unsubscribeWebhook(response.webhookId, context.auth.secret_text);
}
},
async test(context) {
const response = await httpClient.sendRequest<{ data: { id: string }[] }>({
method: HttpMethod.GET,
url: 'https://api.stripe.com/v1/subscriptions',
headers: {
Authorization: 'Bearer ' + context.auth.secret_text,
'Content-Type': 'application/x-www-form-urlencoded',
},
queryParams: {
limit: '5',
},
});
if (isEmpty(response.body) || isEmpty(response.body.data)) return [];
return response.body.data;
},
async run(context) {
const payloadBody = context.payload.body as PayloadBody;
return [payloadBody.data.object];
},
});
type PayloadBody = {
data: {
object: unknown;
};
};
interface WebhookInformation {
webhookId: string;
}

View File

@@ -0,0 +1,208 @@
import { createTrigger } from '@activepieces/pieces-framework';
import { TriggerStrategy } from '@activepieces/pieces-framework';
import { stripeCommon } from '../common';
import { stripeAuth } from '../..';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
import { isEmpty } from '@activepieces/shared';
export const stripePaymentFailed = createTrigger({
auth: stripeAuth,
name: 'payment_failed',
displayName: 'Payment Failed',
description: 'Triggers when a payment fails',
props: {},
sampleData: {
id: 'ch_3MWMPQKZ0dZRqLEK063rxD7q',
object: 'charge',
amount: 100000,
amount_captured: 0,
amount_refunded: 0,
application: null,
application_fee: null,
application_fee_amount: null,
balance_transaction: null,
billing_details: {
address: {
city: null,
country: null,
line1: null,
line2: null,
postal_code: '12321',
state: null,
},
email: null,
name: null,
phone: null,
},
calculated_statement_descriptor: 'WWW.ACTIVEPIECES.COM',
captured: false,
created: 1675181413,
currency: 'usd',
customer: 'cus_NGtvUQ18FJXcGI',
description: 'Failed Payment',
destination: null,
dispute: null,
disputed: false,
failure_balance_transaction: null,
failure_code: 'card_declined',
failure_message: 'Your card was declined.',
fraud_details: {},
invoice: null,
livemode: false,
metadata: {},
on_behalf_of: null,
order: null,
outcome: {
network_status: 'declined_by_network',
reason: 'generic_decline',
risk_level: 'normal',
risk_score: 60,
seller_message:
'The bank did not return any further details with this decline.',
type: 'issuer_declined',
},
paid: false,
payment_intent: 'pi_3MWMPQKZ0dZRqLEK0Nsc6WhL',
payment_method: 'src_1MWMPQKZ0dZRqLEKuQ83wmZI',
payment_method_details: {
card: {
brand: 'visa',
checks: {
address_line1_check: null,
address_postal_code_check: 'pass',
cvc_check: 'pass',
},
country: 'US',
exp_month: 12,
exp_year: 2031,
fingerprint: 'mtYxM2Q4edpEt8Pw',
funding: 'credit',
installments: null,
last4: '0341',
mandate: null,
network: 'visa',
three_d_secure: null,
wallet: null,
},
type: 'card',
},
receipt_email: null,
receipt_number: null,
receipt_url: null,
refunded: false,
refunds: {
object: 'list',
data: [],
has_more: false,
total_count: 0,
url: '/v1/charges/ch_3MWMPQKZ0dZRqLEK063rxD7q/refunds',
},
review: null,
shipping: null,
source: {
id: 'src_1MWMPQKZ0dZRqLEKuQ83wmZI',
object: 'source',
amount: null,
card: {
exp_month: 12,
exp_year: 2031,
last4: '0341',
country: 'US',
brand: 'Visa',
address_zip_check: 'pass',
cvc_check: 'pass',
funding: 'credit',
fingerprint: 'mtYxM2Q4edpEt8Pw',
three_d_secure: 'optional',
name: null,
address_line1_check: null,
tokenization_method: null,
dynamic_last4: null,
},
client_secret: 'src_client_secret_TlLkl6IvhCvmbx8Cz12YNDVb',
created: 1675181413,
currency: null,
flow: 'none',
livemode: false,
metadata: {},
owner: {
address: {
city: null,
country: null,
line1: null,
line2: null,
postal_code: '12321',
state: null,
},
email: null,
name: null,
phone: null,
verified_address: null,
verified_email: null,
verified_name: null,
verified_phone: null,
},
statement_descriptor: null,
status: 'chargeable',
type: 'card',
usage: 'reusable',
},
source_transfer: null,
statement_descriptor: 'www.activepieces.com',
statement_descriptor_suffix: null,
status: 'failed',
transfer_data: null,
transfer_group: null,
},
type: TriggerStrategy.WEBHOOK,
async onEnable(context) {
const webhook = await stripeCommon.subscribeWebhook(
'charge.failed',
context.webhookUrl,
context.auth.secret_text
);
await context.store?.put<WebhookInformation>('_payment_failed_trigger', {
webhookId: webhook.id,
});
},
async onDisable(context) {
const response = await context.store?.get<WebhookInformation>(
'_payment_failed_trigger'
);
if (response !== null && response !== undefined) {
await stripeCommon.unsubscribeWebhook(response.webhookId, context.auth.secret_text);
}
},
async test(context) {
const response = await httpClient.sendRequest<{ data: { id: string }[] }>({
method: HttpMethod.GET,
url: 'https://api.stripe.com/v1/charges',
headers: {
Authorization: 'Bearer ' + context.auth.secret_text,
'Content-Type': 'application/x-www-form-urlencoded',
},
queryParams: {
status: 'failed',
limit: '5',
},
});
if (isEmpty(response.body) || isEmpty(response.body.data)) return [];
return response.body.data;
},
async run(context) {
const payloadBody = context.payload.body as PayloadBody;
return [payloadBody.data.object];
},
});
type PayloadBody = {
data: {
object: unknown;
};
};
interface WebhookInformation {
webhookId: string;
}

View File

@@ -0,0 +1,147 @@
import {
createTrigger,
TriggerStrategy,
Property,
} from '@activepieces/pieces-framework';
import { stripeCommon } from '../common';
import { StripeWebhookInformation } from '../common/types';
import { stripeAuth } from '../..';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
import { isEmpty } from '@activepieces/shared';
type StripeWebhookPayload = {
data: {
object: {
[key: string]: unknown;
};
};
};
export const stripeUpdatedSubscription = createTrigger({
auth: stripeAuth,
name: 'updated_subscription',
displayName: 'Updated Subscription',
description: 'Fires when an existing subscription is changed.',
props: {
status: Property.StaticDropdown({
displayName: 'New Status',
description:
'Only trigger when the subscription is updated to this status.',
required: false,
options: {
options: [
{ label: 'Incomplete', value: 'incomplete' },
{ label: 'Incomplete - Expired', value: 'incomplete_expired' },
{ label: 'Trialing', value: 'trialing' },
{ label: 'Active', value: 'active' },
{ label: 'Past Due', value: 'past_due' },
{ label: 'Canceled', value: 'canceled' },
{ label: 'Unpaid', value: 'unpaid' },
{ label: 'Paused', value: 'paused' },
],
},
}),
customer: Property.ShortText({
displayName: 'Customer ID',
description:
'Only trigger for subscriptions belonging to this customer ID (e.g., `cus_...`).',
required: false,
}),
},
sampleData: {
id: 'sub_1MowQVLkdIwHu7ixeRlqHVzs',
object: 'subscription',
application: null,
application_fee_percent: null,
automatic_tax: {
enabled: false,
},
billing_cycle_anchor: 1679609767,
cancel_at_period_end: false,
canceled_at: null,
collection_method: 'charge_automatically',
created: 1679609767,
currency: 'usd',
customer: 'cus_Na6dX7aXxi11N4',
items: {
object: 'list',
data: [
{
id: 'si_Na6dzxczY5fwHx',
object: 'subscription_item',
price: {
id: 'price_1MowQULkdIwHu7ixraBm864M',
object: 'price',
product: 'prod_Na6dGcTsmU0I4R',
unit_amount: 2000,
},
quantity: 1,
},
],
},
latest_invoice: 'in_1MowQWLkdIwHu7ixuzkSPfKd',
livemode: false,
metadata: { plan: 'premium' },
start_date: 1679609767,
status: 'active',
},
type: TriggerStrategy.WEBHOOK,
async onEnable(context) {
const webhook = await stripeCommon.subscribeWebhook(
'customer.subscription.updated',
context.webhookUrl,
context.auth.secret_text
);
await context.store.put<StripeWebhookInformation>(
'_updated_subscription_trigger',
{
webhookId: webhook.id,
}
);
},
async onDisable(context) {
const webhookInfo = await context.store.get<StripeWebhookInformation>(
'_updated_subscription_trigger'
);
if (webhookInfo !== null && webhookInfo !== undefined) {
await stripeCommon.unsubscribeWebhook(
webhookInfo.webhookId,
context.auth.secret_text
);
}
},
async test(context) {
const response = await httpClient.sendRequest<{ data: { id: string }[] }>({
method: HttpMethod.GET,
url: 'https://api.stripe.com/v1/charges',
headers: {
Authorization: 'Bearer ' + context.auth.secret_text,
'Content-Type': 'application/x-www-form-urlencoded',
},
queryParams: {
status: 'failed',
limit: '5',
},
});
if (isEmpty(response.body) || isEmpty(response.body.data)) return [];
return response.body.data;
},
async run(context) {
const payloadBody = context.payload.body as StripeWebhookPayload;
const subscriptionObject = payloadBody.data.object;
const { status, customer } = context.propsValue;
if (status && subscriptionObject['status'] !== status) {
return [];
}
if (customer && subscriptionObject['customer'] !== customer) {
return [];
}
return [subscriptionObject];
},
});