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:
@@ -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);
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -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;
|
||||
},
|
||||
});
|
||||
@@ -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;
|
||||
},
|
||||
});
|
||||
@@ -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;
|
||||
},
|
||||
});
|
||||
@@ -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 customer’s 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);
|
||||
},
|
||||
});
|
||||
@@ -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;
|
||||
},
|
||||
});
|
||||
@@ -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 product’s name, meant to be displayable to the customer.',
|
||||
required: true,
|
||||
}),
|
||||
description: Property.LongText({
|
||||
displayName: 'Description',
|
||||
description:
|
||||
'The product’s 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;
|
||||
},
|
||||
});
|
||||
@@ -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;
|
||||
},
|
||||
});
|
||||
@@ -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;
|
||||
},
|
||||
});
|
||||
@@ -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,
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -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;
|
||||
},
|
||||
});
|
||||
@@ -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;
|
||||
},
|
||||
});
|
||||
@@ -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;
|
||||
},
|
||||
});
|
||||
@@ -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;
|
||||
},
|
||||
});
|
||||
@@ -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;
|
||||
},
|
||||
});
|
||||
@@ -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;
|
||||
},
|
||||
});
|
||||
@@ -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,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -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 customer’s 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;
|
||||
},
|
||||
});
|
||||
@@ -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",
|
||||
};
|
||||
}
|
||||
},
|
||||
}),
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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];
|
||||
},
|
||||
});
|
||||
@@ -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];
|
||||
},
|
||||
});
|
||||
@@ -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];
|
||||
},
|
||||
});
|
||||
@@ -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];
|
||||
},
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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];
|
||||
},
|
||||
});
|
||||
@@ -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];
|
||||
},
|
||||
});
|
||||
@@ -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];
|
||||
},
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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];
|
||||
},
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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];
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user