From ea0cd8815920e6e4a8a50e639f9289606b39fef6 Mon Sep 17 00:00:00 2001 From: GarvShah29 Date: Tue, 23 Jun 2026 19:03:53 +0530 Subject: [PATCH 1/2] Add Stadium app integration with actions Introduce the Stadium Pipedream app with client credentials auth and actions for stores, orders, points, shipments, and automations. Co-authored-by: Cursor --- components/stadium/README.md | 62 ++++ .../check-automation-order-status.mjs | 39 +++ .../actions/checkout-order/checkout-order.mjs | 38 ++ .../create-automation-order.mjs | 62 ++++ .../actions/create-order/create-order.mjs | 101 ++++++ .../get-order-details/get-order-details.mjs | 31 ++ .../get-shipment-status.mjs | 31 ++ .../get-store-products/get-store-products.mjs | 91 +++++ .../get-user-profile/get-user-profile.mjs | 24 ++ .../actions/list-stores/list-stores.mjs | 27 ++ .../actions/send-points/send-points.mjs | 114 ++++++ components/stadium/package.json | 18 + components/stadium/stadium.app.mjs | 330 ++++++++++++++++++ pnpm-lock.yaml | 6 + 14 files changed, 974 insertions(+) create mode 100644 components/stadium/README.md create mode 100644 components/stadium/actions/check-automation-order-status/check-automation-order-status.mjs create mode 100644 components/stadium/actions/checkout-order/checkout-order.mjs create mode 100644 components/stadium/actions/create-automation-order/create-automation-order.mjs create mode 100644 components/stadium/actions/create-order/create-order.mjs create mode 100644 components/stadium/actions/get-order-details/get-order-details.mjs create mode 100644 components/stadium/actions/get-shipment-status/get-shipment-status.mjs create mode 100644 components/stadium/actions/get-store-products/get-store-products.mjs create mode 100644 components/stadium/actions/get-user-profile/get-user-profile.mjs create mode 100644 components/stadium/actions/list-stores/list-stores.mjs create mode 100644 components/stadium/actions/send-points/send-points.mjs create mode 100644 components/stadium/package.json create mode 100644 components/stadium/stadium.app.mjs diff --git a/components/stadium/README.md b/components/stadium/README.md new file mode 100644 index 0000000000000..735b53e411449 --- /dev/null +++ b/components/stadium/README.md @@ -0,0 +1,62 @@ +# Overview + +Stadium is a global recognition, gifting, and branded swag platform. Use the Stadium API to embed gift, rewards, and branded swag capabilities into your own applications. + +With Stadium's Pipedream integration, you can: + +- Manage stores and browse product catalogs +- Place and checkout orders programmatically +- Send points and rewards to recipients +- Create and monitor automation orders +- Track order shipment status + +## Limitations + +- Stadium's API is available only to customers on Business and Enterprise packages. +- Access tokens are valid for 24 hours and must be refreshed. +- Automation orders may take up to 30 minutes to process. + +# Example Use Cases + +1. **Automated Employee Rewards** — Trigger Stadium point sends when milestones are reached in your HR system. +2. **E-commerce Integration** — Automatically place Stadium orders when customers make purchases on your platform. +3. **Event-Driven Gifting** — Send branded swag to attendees after event registration via webhook. +4. **Order Tracking** — Monitor shipment status and notify recipients when their gifts are on the way. +5. **Catalog Sync** — Keep your product catalog in sync with Stadium's latest offerings. + +# Getting Started + +## Creating a Stadium Account + +1. Sign up for a free Stadium account at [bystadium.com](https://www.bystadium.com/). +2. Create a Stadium Shop and curate your product catalog. +3. Set up your funding source (wallet funds for order payments). +4. Contact [engineering@bystadium.com](mailto:engineering@bystadium.com) to receive API credentials. + +## Connecting to Pipedream + +1. In your Pipedream workflow, search for the **Stadium** app. +2. Click **Connect Stadium** and enter your Client ID and Client Secret. +3. Pipedream will handle token generation and renewal automatically. + +## Sandbox Environment + +For testing, Stadium provides a sandbox environment at `https://api.preprod.bystadium.com`. Contact [engineering@bystadium.com](mailto:engineering@bystadium.com) for sandbox credentials. + +# Troubleshooting + +## Authentication Errors + +If you receive a `401 Unauthorized` error, verify that: +- Your Client ID and Client Secret are correct. +- Your account is on a Business or Enterprise plan. +- Your access token has not expired (tokens are valid for 24 hours). + +## Order Errors + +- `422` errors typically indicate invalid request data. Check that store numbers, product IDs, and addresses are correctly formatted. +- `404` errors mean the specified resource (order, store, automation) was not found. + +## Automation Orders + +Automation orders may take up to 30 minutes to process. Use the **Check Automation Order Status** action to monitor progress. diff --git a/components/stadium/actions/check-automation-order-status/check-automation-order-status.mjs b/components/stadium/actions/check-automation-order-status/check-automation-order-status.mjs new file mode 100644 index 0000000000000..e9f3a1dc74add --- /dev/null +++ b/components/stadium/actions/check-automation-order-status/check-automation-order-status.mjs @@ -0,0 +1,39 @@ +import stadium from "../../stadium.app.mjs"; + +export default { + key: "stadium-check-automation-order-status", + name: "Check Automation Order Status", + description: "Check the status of an automation order. [See the documentation](https://api.bystadium.com/api/v2/docs#tag/Automation-management/operation/checkOrderStatus)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: false, + readOnlyHint: true, + }, + props: { + stadium, + apiKey: { + type: "string", + label: "Automation API Key", + description: "API key of the webhook automation (e.g., `ryfMiGzMp8ZTisWYbZUhjkVL`)", + secret: true, + }, + orderIdentifier: { + type: "string", + label: "Order Identifier", + description: "Identifier of the automation order to check", + }, + }, + async run({ $ }) { + const response = await this.stadium.checkAutomationOrderStatus({ + $, + apiKey: this.apiKey, + data: { + order_identifier: this.orderIdentifier, + }, + }); + $.export("$summary", `Successfully retrieved status for automation order ${this.orderIdentifier}`); + return response; + }, +}; diff --git a/components/stadium/actions/checkout-order/checkout-order.mjs b/components/stadium/actions/checkout-order/checkout-order.mjs new file mode 100644 index 0000000000000..30eef720ea18e --- /dev/null +++ b/components/stadium/actions/checkout-order/checkout-order.mjs @@ -0,0 +1,38 @@ +import stadium from "../../stadium.app.mjs"; + +export default { + key: "stadium-checkout-order", + name: "Checkout Order", + description: "Checkout a placed order using the specified payment method. [See the documentation](https://api.bystadium.com/api/v2/docs#tag/Order-management/operation/orderCheckout)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + stadium, + orderNumber: { + propDefinition: [ + stadium, + "orderNumber", + ], + }, + paymentMethod: { + propDefinition: [ + stadium, + "paymentMethod", + ], + }, + }, + async run({ $ }) { + const response = await this.stadium.checkoutOrder({ + $, + orderNumber: this.orderNumber, + paymentMethod: this.paymentMethod, + }); + $.export("$summary", `Successfully checked out order ${this.orderNumber}`); + return response; + }, +}; diff --git a/components/stadium/actions/create-automation-order/create-automation-order.mjs b/components/stadium/actions/create-automation-order/create-automation-order.mjs new file mode 100644 index 0000000000000..1de35875ec62d --- /dev/null +++ b/components/stadium/actions/create-automation-order/create-automation-order.mjs @@ -0,0 +1,62 @@ +import stadium from "../../stadium.app.mjs"; + +export default { + key: "stadium-create-automation-order", + name: "Create Automation Order", + description: "Place an order for a webhook automation. May take up to 30 minutes to process. [See the documentation](https://api.bystadium.com/api/v2/docs#tag/Automation-management/operation/createAutomationOrder)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + stadium, + apiKey: { + type: "string", + label: "Automation API Key", + description: "API key of the webhook automation (e.g., `ryfMiGzMp8ZTisWYbZUhjkVL`)", + secret: true, + }, + contactEmails: { + type: "string[]", + label: "Contact Emails", + description: "Email addresses of recipients", + }, + recipientMessage: { + type: "string", + label: "Recipient Message", + description: "Message shown to the recipient at redemption. If not set, the message configured during automation setup will be used", + optional: true, + }, + senderName: { + type: "string", + label: "Sender Name", + description: "Name of the sender. If not provided, the sender name configured during automation setup will be used", + optional: true, + }, + budget: { + type: "integer", + label: "Budget", + description: "Amount of points to be gifted. If not provided, the points budget configured during automation setup will be used", + optional: true, + }, + }, + async run({ $ }) { + const data = { + contact_emails: this.contactEmails, + }; + if (this.recipientMessage) data.recipient_message = this.recipientMessage; + if (this.senderName) data.sender_name = this.senderName; + if (this.budget) data.budget = this.budget; + + const response = await this.stadium.createAutomationOrder({ + $, + apiKey: this.apiKey, + data, + }); + $.export("$summary", `Successfully created automation order — Identifier: ${response.identifier}`); + return response; + }, +}; diff --git a/components/stadium/actions/create-order/create-order.mjs b/components/stadium/actions/create-order/create-order.mjs new file mode 100644 index 0000000000000..bd85bc058927d --- /dev/null +++ b/components/stadium/actions/create-order/create-order.mjs @@ -0,0 +1,101 @@ +import stadium from "../../stadium.app.mjs"; + +export default { + key: "stadium-create-order", + name: "Create Order", + description: "Place a new order against a Stadium Shop. [See the documentation](https://api.bystadium.com/api/v2/docs#tag/Order-management/operation/createOrder)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + stadium, + storeNumber: { + propDefinition: [ + stadium, + "storeNumber", + ], + }, + countryIso: { + type: "string", + label: "Country ISO", + description: "ISO code of the country (e.g., `US`)", + }, + firstName: { + type: "string", + label: "First Name", + description: "Recipient's first name", + }, + lastName: { + type: "string", + label: "Last Name", + description: "Recipient's last name", + }, + phone: { + type: "string", + label: "Phone", + description: "Recipient's phone number", + optional: true, + }, + street1: { + type: "string", + label: "Address Line 1", + description: "First line of the shipping address", + }, + street2: { + type: "string", + label: "Address Line 2", + description: "Second line of the shipping address", + optional: true, + }, + city: { + type: "string", + label: "City", + description: "City", + }, + stateName: { + type: "string", + label: "State", + description: "State name", + }, + zipCode: { + type: "string", + label: "Zip Code", + description: "Zip code", + }, + products: { + type: "string", + label: "Products", + description: "JSON array of products to order. Each product should have `id` (variant ID, e.g., `10/1/9`), `quantity`, and `product_type` (e.g., `Spree::Product`). Example: `[{\"id\":\"10/1/9\",\"quantity\":1,\"product_type\":\"Spree::Product\"}]`", + }, + }, + async run({ $ }) { + const products = typeof this.products === "string" + ? JSON.parse(this.products) + : this.products; + + const response = await this.stadium.createOrder({ + $, + data: { + store_number: this.storeNumber, + country_iso: this.countryIso, + address: { + first_name: this.firstName, + last_name: this.lastName, + phone: this.phone, + zip_code: this.zipCode, + street_1: this.street1, + street_2: this.street2, + city: this.city, + state_name: this.stateName, + }, + products, + }, + }); + $.export("$summary", `Successfully created order ${response.number}`); + return response; + }, +}; diff --git a/components/stadium/actions/get-order-details/get-order-details.mjs b/components/stadium/actions/get-order-details/get-order-details.mjs new file mode 100644 index 0000000000000..277d19dcc253f --- /dev/null +++ b/components/stadium/actions/get-order-details/get-order-details.mjs @@ -0,0 +1,31 @@ +import stadium from "../../stadium.app.mjs"; + +export default { + key: "stadium-get-order-details", + name: "Get Order Details", + description: "Get details for a placed order. [See the documentation](https://api.bystadium.com/api/v2/docs#tag/Order-management/operation/orderDetails)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: false, + readOnlyHint: true, + }, + props: { + stadium, + orderNumber: { + propDefinition: [ + stadium, + "orderNumber", + ], + }, + }, + async run({ $ }) { + const response = await this.stadium.getOrderDetails({ + $, + orderNumber: this.orderNumber, + }); + $.export("$summary", `Successfully retrieved details for order ${this.orderNumber}`); + return response; + }, +}; diff --git a/components/stadium/actions/get-shipment-status/get-shipment-status.mjs b/components/stadium/actions/get-shipment-status/get-shipment-status.mjs new file mode 100644 index 0000000000000..9e7727e34687a --- /dev/null +++ b/components/stadium/actions/get-shipment-status/get-shipment-status.mjs @@ -0,0 +1,31 @@ +import stadium from "../../stadium.app.mjs"; + +export default { + key: "stadium-get-shipment-status", + name: "Get Shipment Status", + description: "Get shipment status and tracking details for a placed order. [See the documentation](https://api.bystadium.com/api/v2/docs#tag/Order-management/operation/orderShipmentDetails)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: false, + readOnlyHint: true, + }, + props: { + stadium, + orderNumber: { + propDefinition: [ + stadium, + "orderNumber", + ], + }, + }, + async run({ $ }) { + const response = await this.stadium.getShipmentStatus({ + $, + orderNumber: this.orderNumber, + }); + $.export("$summary", `Successfully retrieved shipment status for order ${this.orderNumber}`); + return response; + }, +}; diff --git a/components/stadium/actions/get-store-products/get-store-products.mjs b/components/stadium/actions/get-store-products/get-store-products.mjs new file mode 100644 index 0000000000000..af9b58a03d041 --- /dev/null +++ b/components/stadium/actions/get-store-products/get-store-products.mjs @@ -0,0 +1,91 @@ +import stadium from "../../stadium.app.mjs"; + +export default { + key: "stadium-get-store-products", + name: "Get Store Products", + description: "Get all products for a store with optional filters. [See the documentation](https://api.bystadium.com/api/v2/docs#tag/Store-management/operation/getStoreProducts)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: false, + readOnlyHint: true, + }, + props: { + stadium, + storeNumber: { + propDefinition: [ + stadium, + "storeNumber", + ], + }, + page: { + propDefinition: [ + stadium, + "page", + ], + }, + perPage: { + propDefinition: [ + stadium, + "perPage", + ], + }, + sortBy: { + type: "string", + label: "Sort By", + description: "Sort order for products (e.g., `price:asc`)", + optional: true, + }, + countryCode: { + propDefinition: [ + stadium, + "countryCode", + ], + }, + color: { + type: "string", + label: "Color", + description: "Filter products by color", + optional: true, + }, + size: { + type: "string", + label: "Size", + description: "Filter products by size", + optional: true, + }, + includeInactiveProducts: { + type: "boolean", + label: "Include Inactive Products", + description: "Include inactive products in the results", + optional: true, + }, + withOutOfStock: { + type: "boolean", + label: "Include Out of Stock", + description: "Include out of stock products in the results", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.stadium.getStoreProducts({ + $, + storeNumber: this.storeNumber, + page: this.page, + perPage: this.perPage, + sortBy: this.sortBy, + countryCode: this.countryCode, + color: this.color, + size: this.size, + includeInactiveProducts: this.includeInactiveProducts, + withOutOfStock: this.withOutOfStock, + }); + const count = response.products?.length ?? 0; + const total = response.meta?.total ?? count; + $.export("$summary", `Successfully retrieved ${count} product${count === 1 + ? "" + : "s"} (${total} total)`); + return response; + }, +}; diff --git a/components/stadium/actions/get-user-profile/get-user-profile.mjs b/components/stadium/actions/get-user-profile/get-user-profile.mjs new file mode 100644 index 0000000000000..32208dae48780 --- /dev/null +++ b/components/stadium/actions/get-user-profile/get-user-profile.mjs @@ -0,0 +1,24 @@ +import stadium from "../../stadium.app.mjs"; + +export default { + key: "stadium-get-user-profile", + name: "Get User Profile", + description: "Get the current user's profile including wallet balance. [See the documentation](https://api.bystadium.com/api/v2/docs#tag/User-management/operation/getUserProfile)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: false, + readOnlyHint: true, + }, + props: { + stadium, + }, + async run({ $ }) { + const response = await this.stadium.getUserProfile({ + $, + }); + $.export("$summary", `Successfully retrieved profile for ${response.email}`); + return response; + }, +}; diff --git a/components/stadium/actions/list-stores/list-stores.mjs b/components/stadium/actions/list-stores/list-stores.mjs new file mode 100644 index 0000000000000..5148838670634 --- /dev/null +++ b/components/stadium/actions/list-stores/list-stores.mjs @@ -0,0 +1,27 @@ +import stadium from "../../stadium.app.mjs"; + +export default { + key: "stadium-list-stores", + name: "List Stores", + description: "Get all stores for the current user. [See the documentation](https://api.bystadium.com/api/v2/docs#tag/Store-management/operation/getStores)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: false, + readOnlyHint: true, + }, + props: { + stadium, + }, + async run({ $ }) { + const response = await this.stadium.listStores({ + $, + }); + const count = response.stores?.length ?? 0; + $.export("$summary", `Successfully retrieved ${count} store${count === 1 + ? "" + : "s"}`); + return response; + }, +}; diff --git a/components/stadium/actions/send-points/send-points.mjs b/components/stadium/actions/send-points/send-points.mjs new file mode 100644 index 0000000000000..1c909787b5852 --- /dev/null +++ b/components/stadium/actions/send-points/send-points.mjs @@ -0,0 +1,114 @@ +import stadium from "../../stadium.app.mjs"; + +export default { + key: "stadium-send-points", + name: "Send Points", + description: "Gift store-specific or Stadium points to recipients. [See the documentation](https://api.bystadium.com/api/v2/docs#tag/Order-management/operation/sendPoints)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + stadium, + storeNumber: { + propDefinition: [ + stadium, + "storeNumber", + ], + description: "Store Number. Optional when sending Stadium points", + optional: true, + }, + contactEmails: { + type: "string", + label: "Contact Emails", + description: "Comma-separated emails of recipients (e.g., `a@b.com,c@d.com`)", + }, + organizerShare: { + type: "integer", + label: "Points Per Recipient", + description: "Number of points to send per recipient", + }, + expectedCount: { + type: "integer", + label: "Expected Recipient Count", + description: "Number of recipients", + }, + sendShopPoints: { + type: "boolean", + label: "Send Shop Points", + description: "Whether to send shop points or Stadium points. Default: `false`", + optional: true, + }, + treatName: { + type: "string", + label: "Treat Name", + description: "Name of the treat", + optional: true, + }, + rlpMessage: { + type: "string", + label: "Recipient Landing Page Message", + description: "Message displayed on the recipient landing page", + optional: true, + }, + paymentMethod: { + type: "string[]", + label: "Payment Method", + description: "Payment method(s) to use", + options: [ + { + label: "Use Wallet Money", + value: "use_wallet_money", + }, + { + label: "Use Global Points", + value: "use_global_point", + }, + ], + optional: true, + }, + autoAcceptPoints: { + type: "boolean", + label: "Auto Accept Points", + description: "Whether the points will be auto accepted, skipping the acceptance flow. Default: `false`", + optional: true, + }, + billingCountry: { + type: "string", + label: "Billing Country", + description: "ISO code for billing country (e.g., `US`)", + optional: true, + }, + billingZipcode: { + type: "string", + label: "Billing Zipcode", + description: "Billing zipcode", + optional: true, + }, + }, + async run({ $ }) { + const data = { + contact_emails: this.contactEmails, + organizer_share: this.organizerShare, + expected_count: this.expectedCount, + }; + if (this.storeNumber) data.store_number = this.storeNumber; + if (this.sendShopPoints != null) data.send_shop_points = this.sendShopPoints; + if (this.treatName) data.treat_name = this.treatName; + if (this.rlpMessage) data.rlp_message = this.rlpMessage; + if (this.paymentMethod) data.payment_method = this.paymentMethod; + if (this.autoAcceptPoints != null) data.auto_accept_points = this.autoAcceptPoints; + if (this.billingCountry) data.billing_country = this.billingCountry; + if (this.billingZipcode) data.billing_zipcode = this.billingZipcode; + + const response = await this.stadium.sendPoints({ + $, + data, + }); + $.export("$summary", `Successfully sent points — Order ${response.number}`); + return response; + }, +}; diff --git a/components/stadium/package.json b/components/stadium/package.json new file mode 100644 index 0000000000000..1f83b860c4ac1 --- /dev/null +++ b/components/stadium/package.json @@ -0,0 +1,18 @@ +{ + "name": "@pipedream/stadium", + "version": "0.0.1", + "description": "Pipedream Stadium Components", + "main": "stadium.app.mjs", + "keywords": [ + "pipedream", + "stadium" + ], + "homepage": "https://pipedream.com/apps/stadium", + "author": "Pipedream (https://pipedream.com/)", + "dependencies": { + "@pipedream/platform": "^3.2.5" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/components/stadium/stadium.app.mjs b/components/stadium/stadium.app.mjs new file mode 100644 index 0000000000000..42cccfa8e8c62 --- /dev/null +++ b/components/stadium/stadium.app.mjs @@ -0,0 +1,330 @@ +import { axios } from "@pipedream/platform"; + +export default { + type: "app", + app: "stadium", + propDefinitions: { + storeNumber: { + type: "string", + label: "Store Number", + description: "The store number (e.g., `S235588918`)", + async options() { + const { stores } = await this.listStores(); + return stores.map((store) => ({ + label: `${store.name} (${store.number})`, + value: store.number, + })); + }, + }, + orderNumber: { + type: "string", + label: "Order Number", + description: "The order number (e.g., `R211027588`)", + }, + countryCode: { + type: "string", + label: "Country Code", + description: "2-digit ISO country code (e.g., `US`, `IN`). Default is `US`", + optional: true, + default: "US", + }, + page: { + type: "integer", + label: "Page", + description: "Page number for pagination", + default: 1, + min: 1, + }, + perPage: { + type: "integer", + label: "Per Page", + description: "Number of items per page", + default: 25, + min: 1, + }, + paymentMethod: { + type: "string", + label: "Payment Method", + description: "Payment method to use for checkout", + options: [ + { + label: "Use Global Points", + value: "use_global_point", + }, + { + label: "Use Wallet Money", + value: "use_wallet_money", + }, + ], + }, + }, + methods: { + /** + * Get the base URL for the Stadium API + * + * @returns {string} The base URL + */ + _baseUrl() { + return "https://api.bystadium.com/api/v2"; + }, + + /** + * Get the client ID from connected account credentials + * + * @returns {string} The client ID + */ + _clientId() { + return this.$auth.client_id; + }, + + /** + * Get the client secret from connected account credentials + * + * @returns {string} The client secret + */ + _clientSecret() { + return this.$auth.client_secret; + }, + + /** + * Fetch a new access token using client credentials + * + * @param {Object} [opts] - Request options + * @param {Object} [opts.$] - Pipedream step context + * @returns {Promise} The access token + */ + async getAccessToken({ $ = this } = {}) { + const res = await axios($, { + url: `${this._baseUrl()}/oauth/token`, + method: "POST", + data: { + client_id: this._clientId(), + client_secret: this._clientSecret(), + }, + }); + const token = res.token ?? res.access_token; + if (!token) { + throw new Error(`No token in auth response: ${JSON.stringify(res)}`); + } + return token; + }, + /** + * Get default headers for API requests + * + * @param {Object} [opts] - Request options + * @param {Object} [opts.$] - Pipedream step context + * @returns {Object} Headers object with Authorization + */ + async _headers({ $ = this } = {}) { + const token = await this.getAccessToken({ $ }); + return { + "Authorization": `Bearer ${token}`, + "Content-Type": "application/json", + }; + }, + /** + * Make an authenticated HTTP request to the Stadium API + * + * @param {Object} opts - Request options + * @param {string} opts.path - API endpoint path + * @param {string} [opts.method=GET] - HTTP method + * @param {Object} [opts.params] - Query parameters + * @param {Object} [opts.data] - Request body data + * @param {Object} [opts.headers] - Additional headers + * @param {Object} [opts.$] - Pipedream step context + * @returns {Object} API response + */ + async _makeRequest({ + $ = this, path, method = "GET", params, data, headers, + }) { + return axios($, { + url: `${this._baseUrl()}${path}`, + method, + headers: { + ...(await this._headers({ $ })), + ...headers, + }, + params, + data, + }); + }, + /** + * Get the current user's profile including wallet balance + * + * @param {Object} [opts] - Additional request options + * @returns {Object} User profile data + */ + async getUserProfile(opts = {}) { + return this._makeRequest({ + path: "/users/me", + ...opts, + }); + }, + /** + * Get all stores for the current user + * + * @param {Object} [opts] - Additional request options + * @returns {Object} Object containing stores array + */ + async listStores(opts = {}) { + return this._makeRequest({ + path: "/stores", + ...opts, + }); + }, + /** + * Get all products for a store + * + * @param {Object} opts - Request options + * @param {string} opts.storeNumber - The store number + * @param {number} [opts.page=1] - Page number + * @param {number} [opts.perPage=25] - Items per page + * @param {string} [opts.sortBy] - Sort order (e.g., `price:asc`) + * @param {string} [opts.countryCode] - 2-digit ISO country code + * @param {string} [opts.color] - Filter by color + * @param {string} [opts.size] - Filter by size + * @param {boolean} [opts.onlyInactiveProducts] - Filter inactive products only + * @param {boolean} [opts.includeInactiveProducts] - Include inactive products + * @param {boolean} [opts.withOutOfStock] - Include out of stock products + * @returns {Object} Object containing meta and products array + */ + async getStoreProducts({ + storeNumber, page, perPage, sortBy, countryCode, color, size, + onlyInactiveProducts, includeInactiveProducts, withOutOfStock, ...opts + }) { + return this._makeRequest({ + path: `/stores/${storeNumber}/products`, + params: { + page, + per_page: perPage, + sort_by: sortBy, + country_code: countryCode, + color, + size, + only_inactive_products: onlyInactiveProducts, + include_inactive_products: includeInactiveProducts, + with_out_of_stock: withOutOfStock, + }, + ...opts, + }); + }, + /** + * Send points to recipients + * + * @param {Object} opts - Request options + * @param {Object} opts.data - Send points request body + * @returns {Object} Send points response with order details + */ + async sendPoints(opts = {}) { + return this._makeRequest({ + path: "/send_points", + method: "POST", + ...opts, + }); + }, + /** + * Create a new order + * + * @param {Object} opts - Request options + * @param {Object} opts.data - Order data including store_number, country_iso, address, products + * @returns {Object} Created order details + */ + async createOrder(opts = {}) { + return this._makeRequest({ + path: "/orders", + method: "POST", + ...opts, + }); + }, + /** + * Checkout a placed order + * + * @param {Object} opts - Request options + * @param {string} opts.orderNumber - The order number + * @param {string} opts.paymentMethod - Payment method + * @returns {Object} Checkout response + */ + async checkoutOrder({ + orderNumber, paymentMethod, ...opts + }) { + return this._makeRequest({ + path: `/orders/${orderNumber}/checkout`, + method: "POST", + params: { + payment_method: paymentMethod, + }, + ...opts, + }); + }, + /** + * Get details for a placed order + * + * @param {Object} opts - Request options + * @param {string} opts.orderNumber - The order number + * @returns {Object} Order details + */ + async getOrderDetails({ + orderNumber, ...opts + }) { + return this._makeRequest({ + path: `/orders/${orderNumber}`, + ...opts, + }); + }, + /** + * Get shipment status and details for a placed order + * + * @param {Object} opts - Request options + * @param {string} opts.orderNumber - The order number + * @returns {Object} Shipment details + */ + async getShipmentStatus({ + orderNumber, ...opts + }) { + return this._makeRequest({ + path: `/orders/${orderNumber}/shipment_status`, + ...opts, + }); + }, + /** + * Create an automation order + * + * @param {Object} opts - Request options + * @param {string} opts.apiKey - API key for the webhook automation + * @param {Object} opts.data - Automation order data + * @returns {Object} Automation order response + */ + async createAutomationOrder({ + apiKey, ...opts + }) { + return this._makeRequest({ + path: "/automations/orders", + method: "POST", + headers: { + api_key: apiKey, + }, + ...opts, + }); + }, + /** + * Check status of an automation order + * + * @param {Object} opts - Request options + * @param {string} opts.apiKey - API key for the webhook automation + * @param {Object} opts.data - Object containing order_identifier + * @returns {Object} Order status response + */ + async checkAutomationOrderStatus({ + apiKey, ...opts + }) { + return this._makeRequest({ + path: "/automations/order_status", + method: "POST", + headers: { + api_key: apiKey, + }, + ...opts, + }); + }, + }, +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5218d7bc93a93..8391ac0a8db33 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15370,6 +15370,12 @@ importers: components/stackshare_api: {} + components/stadium: + dependencies: + '@pipedream/platform': + specifier: ^3.2.5 + version: 3.3.0 + components/stammer_ai: dependencies: '@pipedream/platform': From 25d2b322127b2fafa96fd679b7369792df6a929b Mon Sep 17 00:00:00 2001 From: GarvShah29 Date: Wed, 24 Jun 2026 08:01:47 +0530 Subject: [PATCH 2/2] Enable openWorldHit to hit on external APIs --- .../check-automation-order-status.mjs | 2 +- .../actions/checkout-order/checkout-order.mjs | 15 ++++++++++++--- .../actions/create-order/create-order.mjs | 16 +++++++++++++--- .../get-order-details/get-order-details.mjs | 2 +- .../get-shipment-status/get-shipment-status.mjs | 2 +- .../get-store-products/get-store-products.mjs | 2 +- .../get-user-profile/get-user-profile.mjs | 2 +- .../stadium/actions/list-stores/list-stores.mjs | 2 +- .../stadium/actions/send-points/send-points.mjs | 12 ++++++------ components/stadium/stadium.app.mjs | 15 --------------- 10 files changed, 37 insertions(+), 33 deletions(-) diff --git a/components/stadium/actions/check-automation-order-status/check-automation-order-status.mjs b/components/stadium/actions/check-automation-order-status/check-automation-order-status.mjs index e9f3a1dc74add..fe7b60589123e 100644 --- a/components/stadium/actions/check-automation-order-status/check-automation-order-status.mjs +++ b/components/stadium/actions/check-automation-order-status/check-automation-order-status.mjs @@ -8,7 +8,7 @@ export default { type: "action", annotations: { destructiveHint: false, - openWorldHint: false, + openWorldHint: true, readOnlyHint: true, }, props: { diff --git a/components/stadium/actions/checkout-order/checkout-order.mjs b/components/stadium/actions/checkout-order/checkout-order.mjs index 30eef720ea18e..a55759c46fff9 100644 --- a/components/stadium/actions/checkout-order/checkout-order.mjs +++ b/components/stadium/actions/checkout-order/checkout-order.mjs @@ -20,9 +20,18 @@ export default { ], }, paymentMethod: { - propDefinition: [ - stadium, - "paymentMethod", + type: "string", + label: "Payment Method", + description: "Payment method to use for checkout", + options: [ + { + label: "Use Global Points", + value: "use_global_point", + }, + { + label: "Use Wallet Money", + value: "use_wallet_money", + }, ], }, }, diff --git a/components/stadium/actions/create-order/create-order.mjs b/components/stadium/actions/create-order/create-order.mjs index bd85bc058927d..70dd11a1d9de9 100644 --- a/components/stadium/actions/create-order/create-order.mjs +++ b/components/stadium/actions/create-order/create-order.mjs @@ -1,3 +1,4 @@ +import { ConfigurationError } from "@pipedream/platform"; import stadium from "../../stadium.app.mjs"; export default { @@ -73,9 +74,18 @@ export default { }, }, async run({ $ }) { - const products = typeof this.products === "string" - ? JSON.parse(this.products) - : this.products; + let products; + if (typeof this.products === "string") { + try { + products = JSON.parse(this.products); + } catch (error) { + throw new ConfigurationError( + `Products must be valid JSON array. Each item needs \`id\` (variant ID, e.g., \`10/1/9\`), \`quantity\`, and \`product_type\` (e.g., \`Spree::Product\`). Example: [{"id":"10/1/9","quantity":1,"product_type":"Spree::Product"}]. Parse error: ${error.message}`, + ); + } + } else { + products = this.products; + } const response = await this.stadium.createOrder({ $, diff --git a/components/stadium/actions/get-order-details/get-order-details.mjs b/components/stadium/actions/get-order-details/get-order-details.mjs index 277d19dcc253f..34909b90aca15 100644 --- a/components/stadium/actions/get-order-details/get-order-details.mjs +++ b/components/stadium/actions/get-order-details/get-order-details.mjs @@ -8,7 +8,7 @@ export default { type: "action", annotations: { destructiveHint: false, - openWorldHint: false, + openWorldHint: true, readOnlyHint: true, }, props: { diff --git a/components/stadium/actions/get-shipment-status/get-shipment-status.mjs b/components/stadium/actions/get-shipment-status/get-shipment-status.mjs index 9e7727e34687a..efbef3579c62b 100644 --- a/components/stadium/actions/get-shipment-status/get-shipment-status.mjs +++ b/components/stadium/actions/get-shipment-status/get-shipment-status.mjs @@ -8,7 +8,7 @@ export default { type: "action", annotations: { destructiveHint: false, - openWorldHint: false, + openWorldHint: true, readOnlyHint: true, }, props: { diff --git a/components/stadium/actions/get-store-products/get-store-products.mjs b/components/stadium/actions/get-store-products/get-store-products.mjs index af9b58a03d041..2fcc47583619b 100644 --- a/components/stadium/actions/get-store-products/get-store-products.mjs +++ b/components/stadium/actions/get-store-products/get-store-products.mjs @@ -8,7 +8,7 @@ export default { type: "action", annotations: { destructiveHint: false, - openWorldHint: false, + openWorldHint: true, readOnlyHint: true, }, props: { diff --git a/components/stadium/actions/get-user-profile/get-user-profile.mjs b/components/stadium/actions/get-user-profile/get-user-profile.mjs index 32208dae48780..39436ba4c0bd0 100644 --- a/components/stadium/actions/get-user-profile/get-user-profile.mjs +++ b/components/stadium/actions/get-user-profile/get-user-profile.mjs @@ -8,7 +8,7 @@ export default { type: "action", annotations: { destructiveHint: false, - openWorldHint: false, + openWorldHint: true, readOnlyHint: true, }, props: { diff --git a/components/stadium/actions/list-stores/list-stores.mjs b/components/stadium/actions/list-stores/list-stores.mjs index 5148838670634..51f279bad8610 100644 --- a/components/stadium/actions/list-stores/list-stores.mjs +++ b/components/stadium/actions/list-stores/list-stores.mjs @@ -8,7 +8,7 @@ export default { type: "action", annotations: { destructiveHint: false, - openWorldHint: false, + openWorldHint: true, readOnlyHint: true, }, props: { diff --git a/components/stadium/actions/send-points/send-points.mjs b/components/stadium/actions/send-points/send-points.mjs index 1c909787b5852..87cc97d24cf45 100644 --- a/components/stadium/actions/send-points/send-points.mjs +++ b/components/stadium/actions/send-points/send-points.mjs @@ -56,17 +56,17 @@ export default { }, paymentMethod: { type: "string[]", - label: "Payment Method", - description: "Payment method(s) to use", + label: "Payment Methods", + description: "Payment method(s) to use. Pass one or both: `use_global_point`, `use_wallet_money`", options: [ - { - label: "Use Wallet Money", - value: "use_wallet_money", - }, { label: "Use Global Points", value: "use_global_point", }, + { + label: "Use Wallet Money", + value: "use_wallet_money", + }, ], optional: true, }, diff --git a/components/stadium/stadium.app.mjs b/components/stadium/stadium.app.mjs index 42cccfa8e8c62..32c1c0b5f71cb 100644 --- a/components/stadium/stadium.app.mjs +++ b/components/stadium/stadium.app.mjs @@ -42,21 +42,6 @@ export default { default: 25, min: 1, }, - paymentMethod: { - type: "string", - label: "Payment Method", - description: "Payment method to use for checkout", - options: [ - { - label: "Use Global Points", - value: "use_global_point", - }, - { - label: "Use Wallet Money", - value: "use_wallet_money", - }, - ], - }, }, methods: { /**