Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import app from "../../paykickstart.app.mjs";

export default {
key: "paykickstart-cancel-subscription",
name: "Cancel Subscription",
description: "Cancels an active subscription in PayKickstart. [See the documentation](https://docs.paykickstart.com/)",
version: "0.0.1",
type: "action",
props: {
app,
invoiceId: {
propDefinition: [
app,
"invoiceId",
],
},
cancelAt: {
type: "integer",
label: "Cancel At",
description: "Unix timestamp for specific cancellation date (optional)",
optional: true,
},
fireEvent: {
type: "integer",
label: "Fire Event",
description: "Whether to trigger cancellation events (1 = yes, 0 = no)",
optional: true,
default: 1,
options: [
{
label: "Yes",
value: 1,
},
{
label: "No",
value: 0,
},
],
},
chargeOverage: {
type: "integer",
label: "Charge Overage",
description: "Calculate and charge overage fees during cancellation (1 = yes, 0 = no)",
optional: true,
default: 0,
options: [
{
label: "Yes",
value: 1,
},
{
label: "No",
value: 0,
},
],
},
},
annotations: {
readOnlyHint: false,
destructiveHint: true,
openWorldHint: true,
idempotentHint: true,
},
async run({ $ }) {
const {
app,
invoiceId,
cancelAt,
fireEvent,
chargeOverage,
} = this;

const data = {
invoice_id: invoiceId,
fire_event: fireEvent,
charge_overage: chargeOverage,
};

if (cancelAt) {
data.cancel_at = cancelAt;
}
Comment on lines +73 to +81
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

@pipedream/platform axios undefined values request payload

💡 Result:

In Pipedream, import { axios } from "@pipedream/platform" ultimately sends JSON request bodies, and JSON cannot represent undefined. When the payload is serialized, any object properties whose values are undefined are omitted (and undefined array items become null). This often looks like “axios dropped my fields” or “payload has missing values”. [1]

Fix options (pick one):

  1. Send null (or "") instead of undefined before calling axios:
const payload = {
  email: steps.prev.$return_value.email ?? null,
  name:  steps.prev.$return_value.name  ?? null,
};
return await axios($, { method: "POST", url, data: payload });
  1. Strip undefined keys explicitly (so you’re intentional about what’s sent):
const raw = { a, b, c };
const data = Object.fromEntries(Object.entries(raw).filter(([,v]) => v !== undefined));
return await axios($, { method: "POST", url, data });
  1. Convert undefinednull recursively via JSON.stringify replacer:
const raw = { a, b, c };
const data = JSON.parse(JSON.stringify(raw, (_k, v) => (v === undefined ? null : v)));
return await axios($, { method: "POST", url, data });

Also make sure you’re calling Pipedream’s axios correctly as axios($, config) (passing $ as the first argument), otherwise you can get confusing behavior/errors. [2][3]

Sources: [1] https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify | [2] https://pipedream.com/docs/http | [3] https://pipedream.com/community/t/help-with-axios-request/2630


Remove the truthy guard on cancelAt.

The if (cancelAt) check blocks valid falsy values like 0 (which may be a valid timestamp). Since @pipedream/platform axios automatically excludes undefined from request payloads, there's no need for the manual truthiness check.

✅ Suggested fix
     const data = {
       invoice_id: invoiceId,
       fire_event: fireEvent,
       charge_overage: chargeOverage,
+      cancel_at: cancelAt,
     };
-
-    if (cancelAt) {
-      data.cancel_at = cancelAt;
-    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const data = {
invoice_id: invoiceId,
fire_event: fireEvent,
charge_overage: chargeOverage,
};
if (cancelAt) {
data.cancel_at = cancelAt;
}
const data = {
invoice_id: invoiceId,
fire_event: fireEvent,
charge_overage: chargeOverage,
cancel_at: cancelAt,
};
🤖 Prompt for AI Agents
In `@components/paykickstart/actions/cancel-subscription/cancel-subscription.mjs`
around lines 73 - 81, The current truthy check "if (cancelAt)" prevents valid
falsy values (e.g., 0) from being included in the payload; remove that guard and
always set data.cancel_at = cancelAt after constructing data (so invoiceId,
fireEvent, chargeOverage remain) — relying on `@pipedream/platform` axios to omit
undefined values. Update the block that builds the "data" object (the variables
data, invoiceId, fireEvent, chargeOverage, cancelAt) accordingly.


const response = await app.cancelSubscription({
$,
data,
});

$.export("$summary", `Successfully cancelled subscription ${invoiceId}`);
return response;
},
};
142 changes: 142 additions & 0 deletions components/paykickstart/actions/create-purchase/create-purchase.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import app from "../../paykickstart.app.mjs";
import constants from "../../common/constants.mjs";

export default {
key: "paykickstart-create-purchase",
name: "Create Purchase",
description: "Creates a new purchase transaction. [See the documentation](https://docs.paykickstart.com/)",
version: "0.0.1",
type: "action",
annotations: {
readOnlyHint: false,
destructiveHint: false,
openWorldHint: true,
},
props: {
app,
product: {
propDefinition: [
app,
"campaignId",
],
},
plan: {
propDefinition: [
app,
"productId",
],
},
firstName: {
type: "string",
label: "First Name",
description: "Buyer's first name",
},
lastName: {
type: "string",
label: "Last Name",
description: "Buyer's last name",
},
email: {
type: "string",
label: "Email",
description: "Buyer's email address",
},
quantity: {
type: "integer",
label: "Quantity",
description: "The quantity of the product being purchased. NOTE: The charge amount will be calculated by multiplying the price and quantity fields before adding tax, shipping etc. If the product's is `0` then don't set this field.",
optional: true,
},
price: {
type: "integer",
label: "Price",
description: "Override the product's price amount for this purchase. If this field is not set, the price from the product's settings is used.",
optional: true,
},
isRecurring: {
type: "boolean",
label: "Is Recurring",
description: "Enable a subscription-based product purchase. This field is not required unless you are setting up the purchase to be a subscription (see fields below).",
optional: true,
},
recurringFreq: {
type: "integer",
label: "Recurring Frequency",
description: "The number of **Recurring Frequency Type** units. If `2` and a **Recurring Frequency Type** is `months`, means the subscription will charge every 2 months. This field is required when **Is Recurring** is set.",
optional: true,
},
recurringFreqType: {
type: "string",
label: "Recurring Frequency Type",
description: "The recurring period for the recurring payment. This field is required when **Is Recurring** is set.",
options: constants.FREQUENCY_TYPES,
optional: true,
},
cycles: {
type: "string",
label: "Cycles",
description: "The number of payments for your subscription. Set as null for an infinite subscription. This field is required when **Is Recurring** is set.",
optional: true,
},
hasTrial: {
type: "boolean",
label: "Has Trial",
description: "Enable trial period for the purchase. Please note this is only currently possible for recurring subscription products. This field is not required unless you which to create a trial period for your product.",
optional: true,
},
trialAmount: {
type: "string",
label: "Trial Amount",
description: "The initial amount to charge for the trial. Amount may be set to `0` for free trial. This field is not required unless you have **Has Trial** enabled.",
optional: true,
},
trialDays: {
type: "integer",
label: "Trial Days",
description: "The number of days before the subscription transactions begin. For example, if you want to set a 14 day free trial, make sure the product is set to recurring in its settings or via the API, then set **Trial Amoun** to `0` and **Trial Days** to `14`. This field is not required unless you have **Has Trial** enabled.",
Comment on lines +47 to +96
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix user-facing typos and align wording with options.

There are a few typos and a pluralization mismatch in the descriptions that can confuse users.

✏️ Suggested text fixes
-      description: "The quantity of the product being purchased. NOTE: The charge amount will be calculated by multiplying the price and quantity fields before adding tax, shipping etc. If the product's is `0` then don't set this field.",
+      description: "The quantity of the product being purchased. NOTE: The charge amount will be calculated by multiplying the price and quantity fields before adding tax, shipping, etc. If the product's price is `0` then don't set this field.",
@@
-      description: "The number of **Recurring Frequency Type** units. If `2` and a **Recurring Frequency Type** is `months`, means the subscription will charge every 2 months. This field is required when **Is Recurring** is set.",
+      description: "The number of **Recurring Frequency Type** units. If `2` and a **Recurring Frequency Type** is `month`, the subscription will charge every 2 months. This field is required when **Is Recurring** is set.",
@@
-      description: "Enable trial period for the purchase. Please note this is only currently possible for recurring subscription products. This field is not required unless you which to create a trial period for your product.",
+      description: "Enable trial period for the purchase. Please note this is only currently possible for recurring subscription products. This field is not required unless you wish to create a trial period for your product.",
@@
-      description: "The number of days before the subscription transactions begin. For example, if you want to set a 14 day free trial, make sure the product is set to recurring in its settings or via the API, then set **Trial Amoun** to `0` and **Trial Days** to `14`. This field is not required unless you have **Has Trial** enabled.",
+      description: "The number of days before the subscription transactions begin. For example, if you want to set a 14 day free trial, make sure the product is set to recurring in its settings or via the API, then set **Trial Amount** to `0` and **Trial Days** to `14`. This field is not required unless you have **Has Trial** enabled.",
🤖 Prompt for AI Agents
In `@components/paykickstart/actions/create-purchase/create-purchase.mjs` around
lines 47 - 96, Fix the user-facing typos and wording mismatches in the field
descriptions: update quantity.description to clarify "If the product's price is
`0` then don't set this field" (replace the incorrect "product's is"), correct
hasTrial.description ("which" → "wish"), fix trialDays.description typo "Trial
Amoun" → "Trial Amount", and ensure recurringFreqType.description/pluralization
matches constants.FREQUENCY_TYPES wording (use plural form like "recurring
periods" or match the options label). Edit the string literals in the
create-purchase schema for the fields quantity, hasTrial, trialDays, and
recurringFreqType to apply these fixes.

optional: true,
},
},
async run({ $ }) {
const {
app,
product,
plan,
firstName,
lastName,
email,
price,
quantity,
isRecurring,
recurringFreq,
recurringFreqType,
cycles,
hasTrial,
trialAmount,
trialDays,
} = this;

const response = await app.createPurchase({
$,
data: {
product,
plan,
first_name: firstName,
last_name: lastName,
email,
price,
quantity,
is_recurring: isRecurring,
recurring_freq: recurringFreq,
recurring_freq_type: recurringFreqType,
cycles,
has_trial: hasTrial,
trial_amount: trialAmount,
trial_days: trialDays,
},
});

$.export("$summary", "Successfully created purchase");
return response;
},
};
9 changes: 9 additions & 0 deletions components/paykickstart/common/constants.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const FREQUENCY_TYPES = [
"day",
"month",
"year",
];

export default {
FREQUENCY_TYPES,
};
107 changes: 103 additions & 4 deletions components/paykickstart/paykickstart.app.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,110 @@
import { axios } from "@pipedream/platform";

export default {
type: "app",
app: "paykickstart",
propDefinitions: {},
propDefinitions: {
campaignId: {
type: "integer",
label: "Campaign ID",
description: "The campaign ID",
},
productId: {
type: "integer",
label: "Product ID",
description: "The campaign ID for the product",
},
invoiceId: {
type: "string",
label: "Invoice ID",
description: "The unique purchase/subscription invoice ID",
async options() {
const { transactions } = await this.getTransactions();

return transactions
.filter((transaction) => transaction.invoice_id)
.map((transaction) => ({
label: `${transaction.invoice_id} - ${transaction.buyer_email || transaction.buyer_name || "No name"} - $${transaction.total_amount || 0}`,
value: transaction.invoice_id,
}));
},
},
affiliateId: {
type: "string",
label: "Affiliate ID",
description: "The affiliate ID or email",
async options({ campaignId }) {
if (!campaignId) {
return [];
}

const affiliates = await this.getAffiliates({
campaignId,
});

return affiliates.map((affiliate) => ({
label: `${affiliate.name || affiliate.email} - ${affiliate.email}`,
value: affiliate.id,
}));
},
},
},
methods: {
// this.$auth contains connected account data
authKeys() {
console.log(Object.keys(this.$auth));
_baseUrl() {
return "https://app.paykickstart.com/api";
},
async _makeRequest({
$ = this, path, ...otherOpts
}) {
const response = await axios($, {
...otherOpts,
debug: true,
url: `${this._baseUrl()}${path}`,
headers: {
"Content-Type": "application/json",
},
data: {
auth_token: this.$auth.api_key,
...otherOpts?.data,
},
});
if (response.error) {
throw new Error(JSON.stringify(response.error, null, 2));
}
return response;
},
createPurchase(opts = {}) {
return this._makeRequest({
method: "POST",
path: "/purchase",
...opts,
});
},
cancelSubscription(opts = {}) {
return this._makeRequest({
method: "POST",
path: "/subscriptions/cancel",
...opts,
});
},
getTransactions(opts = {}) {
return this._makeRequest({
method: "POST",
path: "/transactions",
...opts,
});
},
getAffiliates({
campaignId, ...opts
}) {
return this._makeRequest({
method: "POST",
path: "/affiliates/all",
data: {
campaign_id: campaignId,
},
...opts,
});
Comment on lines +97 to +107
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Prevent campaign_id from being overwritten by opts.data.

Spreading ...opts after data will drop campaign_id if callers provide opts.data. Merge data explicitly and spread the rest.

♻️ Suggested refactor
-    getAffiliates({
-      campaignId, ...opts
-    }) {
+    getAffiliates({ campaignId, ...opts }) {
+      const { data, ...rest } = opts;
       return this._makeRequest({
         method: "POST",
         path: "/affiliates/all",
         data: {
           campaign_id: campaignId,
+          ...data,
         },
-        ...opts,
+        ...rest,
       });
     },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
getAffiliates({
campaignId, ...opts
}) {
return this._makeRequest({
method: "POST",
path: "/affiliates/all",
data: {
campaign_id: campaignId,
},
...opts,
});
getAffiliates({ campaignId, ...opts }) {
const { data, ...rest } = opts;
return this._makeRequest({
method: "POST",
path: "/affiliates/all",
data: {
campaign_id: campaignId,
...data,
},
...rest,
});
},
🤖 Prompt for AI Agents
In `@components/paykickstart/paykickstart.app.mjs` around lines 97 - 107, The
getAffiliates method currently passes data: { campaign_id: campaignId } then
spreads ...opts which can contain data and overwrite campaign_id; to fix, merge
data explicitly before spreading opts by constructing a merged data object
(e.g., merge opts.data with { campaign_id: campaignId }) and pass that merged
data into this._makeRequest, while still spreading other opts properties; update
the getAffiliates function to build mergedData = { ...(opts?.data || {}),
campaign_id: campaignId } (or ensure campaign_id takes precedence) and use
mergedData in the call to this._makeRequest so campaign_id is never lost.

},
},
};
Loading