diff --git a/packages/backend/src/apps/stripe/assets/favicon.svg b/packages/backend/src/apps/stripe/assets/favicon.svg
new file mode 100644
index 00000000..25d00aaa
--- /dev/null
+++ b/packages/backend/src/apps/stripe/assets/favicon.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/packages/backend/src/apps/stripe/auth/index.ts b/packages/backend/src/apps/stripe/auth/index.ts
new file mode 100644
index 00000000..f25205f1
--- /dev/null
+++ b/packages/backend/src/apps/stripe/auth/index.ts
@@ -0,0 +1,31 @@
+import verifyCredentials from "./verify-credentials";
+import isStillVerified from "./is-still-verified";
+
+export default {
+ fields: [
+ {
+ key: 'secretKey',
+ label: 'Secret Key',
+ type: 'string' as const,
+ required: true,
+ readOnly: false,
+ value: null,
+ placeholder: null,
+ description: null,
+ clickToCopy: false,
+ },
+ {
+ key: 'displayName',
+ label: 'Account Name',
+ type: 'string' as const,
+ required: true,
+ readOnly: false,
+ value: null,
+ placeholder: null,
+ description: 'The display name that identifies this stripe connection - most likely the associated account name',
+ clickToCopy: false,
+ },
+ ],
+ verifyCredentials,
+ isStillVerified
+};
\ No newline at end of file
diff --git a/packages/backend/src/apps/stripe/auth/is-still-verified.ts b/packages/backend/src/apps/stripe/auth/is-still-verified.ts
new file mode 100644
index 00000000..4470a643
--- /dev/null
+++ b/packages/backend/src/apps/stripe/auth/is-still-verified.ts
@@ -0,0 +1,9 @@
+import { IGlobalVariable } from '@automatisch/types';
+import verifyCredentials from "./verify-credentials";
+
+const isStillVerified = async ($: IGlobalVariable) => {
+ await verifyCredentials($);
+ return true;
+};
+
+export default isStillVerified;
diff --git a/packages/backend/src/apps/stripe/auth/verify-credentials.ts b/packages/backend/src/apps/stripe/auth/verify-credentials.ts
new file mode 100644
index 00000000..1297d9df
--- /dev/null
+++ b/packages/backend/src/apps/stripe/auth/verify-credentials.ts
@@ -0,0 +1,12 @@
+import { IGlobalVariable } from '@automatisch/types';
+
+const verifyCredentials = async ($: IGlobalVariable) => {
+ await $.http.get(
+ `/v1/events`,
+ );
+ await $.auth.set({
+ screenName: $.auth.data?.displayName,
+ });
+};
+
+export default verifyCredentials;
diff --git a/packages/backend/src/apps/stripe/common/add-auth-header.ts b/packages/backend/src/apps/stripe/common/add-auth-header.ts
new file mode 100644
index 00000000..2e393e9c
--- /dev/null
+++ b/packages/backend/src/apps/stripe/common/add-auth-header.ts
@@ -0,0 +1,8 @@
+import {TBeforeRequest} from "@automatisch/types";
+
+const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
+ requestConfig.headers['Authorization'] = `Bearer ${$.auth.data?.secretKey}`
+ return requestConfig
+}
+
+export default addAuthHeader;
\ No newline at end of file
diff --git a/packages/backend/src/apps/stripe/index.ts b/packages/backend/src/apps/stripe/index.ts
new file mode 100644
index 00000000..505f13bf
--- /dev/null
+++ b/packages/backend/src/apps/stripe/index.ts
@@ -0,0 +1,19 @@
+import defineApp from "../../helpers/define-app";
+import addAuthHeader from "./common/add-auth-header";
+import auth from "./auth"
+import triggers from "./triggers"
+
+export default defineApp({
+ name: 'Stripe',
+ key: 'stripe',
+ iconUrl: '{BASE_URL}/apps/stripe/assets/favicon.svg',
+ authDocUrl: 'https://automatisch.io/docs/apps/stripe/connection',
+ supportsConnections: true,
+ baseUrl: 'https://stripe.com',
+ apiBaseUrl: 'https://api.stripe.com',
+ primaryColor: '635bff',
+ beforeRequest: [addAuthHeader],
+ auth,
+ triggers,
+ actions: [],
+})
\ No newline at end of file
diff --git a/packages/backend/src/apps/stripe/triggers/balance-transaction/get-balance-transactions.ts b/packages/backend/src/apps/stripe/triggers/balance-transaction/get-balance-transactions.ts
new file mode 100644
index 00000000..d70f1bfe
--- /dev/null
+++ b/packages/backend/src/apps/stripe/triggers/balance-transaction/get-balance-transactions.ts
@@ -0,0 +1,32 @@
+import {IGlobalVariable, IJSONObject} from "@automatisch/types";
+import {URLSearchParams} from "url";
+import {isEmpty, omitBy} from "lodash";
+
+const getBalanceTransactions = async ($: IGlobalVariable) => {
+ let response;
+ let lastId = undefined;
+
+ do {
+ const params: IJSONObject = {
+ starting_after: lastId,
+ ending_before: $.flow.lastInternalId
+ }
+ const queryParams = new URLSearchParams(omitBy(params, isEmpty))
+ const requestPath = `/v1/balance_transactions${
+ queryParams.toString() ? `?${queryParams.toString()}` : ''
+ }`;
+
+ response = (await $.http.get(requestPath)).data
+ for (const entry of response.data) {
+ $.pushTriggerItem({
+ raw: entry,
+ meta: {
+ internalId: entry.id as string
+ }
+ })
+ lastId = entry.id
+ }
+ } while (response.has_more)
+};
+
+export default getBalanceTransactions;
\ No newline at end of file
diff --git a/packages/backend/src/apps/stripe/triggers/balance-transaction/index.ts b/packages/backend/src/apps/stripe/triggers/balance-transaction/index.ts
new file mode 100644
index 00000000..27f410ea
--- /dev/null
+++ b/packages/backend/src/apps/stripe/triggers/balance-transaction/index.ts
@@ -0,0 +1,12 @@
+import defineTrigger from "../../../../helpers/define-trigger";
+import getBalanceTransactions from "./get-balance-transactions";
+
+export default defineTrigger({
+ name: 'New Balance Transactions',
+ key: 'newBalanceTransactions',
+ description: 'Triggers when a new transaction is processed (refund, payout, adjustment, ...)',
+ pollInterval: 15,
+ async run($) {
+ await getBalanceTransactions($)
+ }
+})
\ No newline at end of file
diff --git a/packages/backend/src/apps/stripe/triggers/index.ts b/packages/backend/src/apps/stripe/triggers/index.ts
new file mode 100644
index 00000000..f1a9d0c8
--- /dev/null
+++ b/packages/backend/src/apps/stripe/triggers/index.ts
@@ -0,0 +1,4 @@
+import balanceTransaction from "./balance-transaction";
+import payouts from "./payouts";
+
+export default [balanceTransaction, payouts];
\ No newline at end of file
diff --git a/packages/backend/src/apps/stripe/triggers/payouts/get-payouts.ts b/packages/backend/src/apps/stripe/triggers/payouts/get-payouts.ts
new file mode 100644
index 00000000..8791f89a
--- /dev/null
+++ b/packages/backend/src/apps/stripe/triggers/payouts/get-payouts.ts
@@ -0,0 +1,32 @@
+import {IGlobalVariable, IJSONObject} from "@automatisch/types";
+import {URLSearchParams} from "url";
+import {isEmpty, omitBy} from "lodash";
+
+const getPayouts = async ($: IGlobalVariable) => {
+ let response;
+ let lastId = undefined;
+
+ do {
+ const params: IJSONObject = {
+ starting_after: lastId,
+ ending_before: $.flow.lastInternalId
+ }
+ const queryParams = new URLSearchParams(omitBy(params, isEmpty))
+ const requestPath = `/v1/payouts${
+ queryParams.toString() ? `?${queryParams.toString()}` : ''
+ }`;
+
+ response = (await $.http.get(requestPath)).data
+ for (const entry of response.data) {
+ $.pushTriggerItem({
+ raw: entry,
+ meta: {
+ internalId: entry.id as string
+ }
+ })
+ lastId = entry.id
+ }
+ } while (response.has_more)
+};
+
+export default getPayouts;
\ No newline at end of file
diff --git a/packages/backend/src/apps/stripe/triggers/payouts/index.ts b/packages/backend/src/apps/stripe/triggers/payouts/index.ts
new file mode 100644
index 00000000..324b09dd
--- /dev/null
+++ b/packages/backend/src/apps/stripe/triggers/payouts/index.ts
@@ -0,0 +1,12 @@
+import defineTrigger from "../../../../helpers/define-trigger";
+import getPayouts from "./get-payouts";
+
+export default defineTrigger({
+ name: 'New Payouts',
+ key: 'newPayouts',
+ description: 'Triggers when a payout (Stripe <-> Bank account) has been updated',
+ pollInterval: 15,
+ async run($) {
+ await getPayouts($)
+ }
+})
\ No newline at end of file
diff --git a/packages/docs/pages/.vitepress/config.js b/packages/docs/pages/.vitepress/config.js
index 118ebfd1..1ea35237 100644
--- a/packages/docs/pages/.vitepress/config.js
+++ b/packages/docs/pages/.vitepress/config.js
@@ -105,6 +105,14 @@ export default defineConfig({
{ text: 'Connection', link: '/apps/smtp/connection' },
],
},
+ {
+ text: 'Stripe',
+ collapsible: true,
+ items: [
+ { text: 'Triggers', link: '/apps/stripe/triggers' },
+ { text: 'Connection', link: '/apps/stripe/connection' },
+ ],
+ },
{
text: 'Twilio',
collapsible: true,
diff --git a/packages/docs/pages/apps/stripe/connection.md b/packages/docs/pages/apps/stripe/connection.md
new file mode 100644
index 00000000..0cd174eb
--- /dev/null
+++ b/packages/docs/pages/apps/stripe/connection.md
@@ -0,0 +1,14 @@
+# Stripe
+
+:::info
+This page explains the steps you need to follow to set up the Stripe connection in Automatisch. If any of the steps are outdated, please let us know!
+:::
+
+:::info
+You are free to use the **Testing secret key** instead of the productive secret key as well.
+:::
+
+1. Go to the [Stripe Dashboard > Developer > API keys](https://dashboard.stripe.com/apikeys)
+2. Click on **Reveal live key** in the table row **Secret key** and copy the now shown secret key
+3. Paste the **Secret key** in the named field in Automatisch and assign a display name for the connection.
+4. Congrats! You can start using the new Stripe connection!
diff --git a/packages/docs/pages/apps/stripe/triggers.md b/packages/docs/pages/apps/stripe/triggers.md
new file mode 100644
index 00000000..dea401bf
--- /dev/null
+++ b/packages/docs/pages/apps/stripe/triggers.md
@@ -0,0 +1,18 @@
+---
+favicon: /favicons/stripe.svg
+items:
+ - name: New Payouts
+ desc: Triggers when stripe sent a payout to a third-party bank account or vice versa.
+ org: Stripe Documentation
+ orgLink: https://stripe.com/docs/api/payouts/object
+ - name: New Balance Transactions
+ desc: Triggers when a fund has been moved through your stripe account.
+ org: Stripe Documentation
+ orgLink: https://stripe.com/docs/api/balance_transactions/object
+---
+
+
+
+
diff --git a/packages/docs/pages/guide/available-apps.md b/packages/docs/pages/guide/available-apps.md
index 854d92e7..5697771f 100644
--- a/packages/docs/pages/guide/available-apps.md
+++ b/packages/docs/pages/guide/available-apps.md
@@ -15,5 +15,6 @@ Following integrations are currently supported by Automatisch.
- [Scheduler](/apps/scheduler/triggers)
- [Slack](/apps/slack/actions)
- [SMTP](/apps/smtp/actions)
+- [Stripe](/apps/stripe/triggers)
- [Twilio](/apps/twilio/triggers)
- [Twitter](/apps/twitter/triggers)
diff --git a/packages/docs/pages/public/favicons/stripe.svg b/packages/docs/pages/public/favicons/stripe.svg
new file mode 100644
index 00000000..25d00aaa
--- /dev/null
+++ b/packages/docs/pages/public/favicons/stripe.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file