Skip to main content

Shopify

Podium syncs products and orders with Shopify stores via OAuth and webhooks.

OAuth Installation

1

Initiate OAuth

Redirect the merchant to the Shopify install endpoint:
GET /api/v1/shopify/install?shop=your-store.myshopify.com
This generates a Shopify OAuth authorization URL with the required scopes.
2

Authorize

Shopify redirects the merchant to grant permissions. Required scopes:
  • read_products, write_products
  • read_orders, write_orders
  • read_inventory
3

Callback

Shopify redirects to /api/v1/shopify/callback with an access token. Podium stores the token in a ShopifyStore record linked to the creator.
4

Initial sync

After OAuth completes, trigger an initial product sync:
POST /api/v1/shopify/sync/manual
This dispatches a shopify-products-sync event that fetches all products from the Shopify store in the background.

Synced Data Mapping

Shopify EntityPodium ModelSync Direction
ProductsShopifyProductProductShopify → Podium
VariantsShopifyVariantProductAttributeVariantShopify → Podium
OptionsShopifyOptionShopify → Podium
Media/ImagesShopifyMediaProductMediaShopify → Podium
OrdersShopifyOrderPodium → Shopify
Product data flows from Shopify into Podium. Order data flows from Podium back to Shopify via the shopify-order-push event.

Webhook Events

Shopify sends webhooks to POST /webhooks/shopify with the X-Shopify-Topic header:
TopicHandler
products/createCreate new ShopifyProduct + linked Product
products/updateUpdate product data, variants, media
products/deleteSoft-delete the linked product
app/uninstalledRemove store connection, clean up tokens

Webhook Verification

import crypto from 'crypto';

const hmac = crypto.createHmac('sha256', SHOPIFY_API_SECRET);
hmac.update(rawBody);
const digest = hmac.digest('base64');

if (digest !== request.headers['x-shopify-hmac-sha256']) {
  throw new Error('Invalid Shopify webhook signature');
}

Troubleshooting

IssueSolution
Products not syncingVerify the Shopify app has read_products scope. Trigger manual sync via /shopify/sync/manual
Duplicate productsCheck if the Shopify product already has a linked ShopifyProduct record
Orders not pushing to ShopifyVerify the store connection is active and the app has write_orders scope
Webhook failuresCheck event logs for failed shopify-products-sync events

Stripe Connect

Creators receive payouts through Stripe Connect Express accounts.

Setup Flow

1

Create Stripe account

POST /api/v1/creator/id/{creatorId}/stripe/account
Creates a Stripe Express account and returns an onboarding URL.
2

Complete onboarding

Redirect the creator to the Stripe onboarding URL. They’ll complete identity verification and bank account setup directly with Stripe.
3

Account activation

Stripe sends an account.updated webhook when onboarding is complete. Podium updates the StripeAccount status to active.

Platform Fee Configuration

Podium takes a platform application fee on every transaction:
STRIPE_PLATFORM_APPLICATION_FEE_BPS=500   # 5% platform fee
The fee is applied when creating the Stripe PaymentIntent:
const paymentIntent = await stripe.paymentIntents.create({
  amount: orderTotal,
  currency: 'usd',
  application_fee_amount: Math.floor(orderTotal * (feeBps / 10000)),
  transfer_data: {
    destination: creatorStripeAccountId
  }
});

Payout Flow

  1. Customer pays → Stripe PaymentIntent succeeds
  2. Platform fee deducted automatically
  3. CreatorPayout record created with PENDING status
  4. After hold period → status changes to ELIGIBLE
  5. payouts-sweep cron transfers eligible payouts to creator’s Connect account
  6. transfer.created webhook confirms → status updates to TRANSFERRED

Onboarding Reminders

If a creator doesn’t complete Stripe onboarding within 48 hours, the stripe-onboarding-reminder cron sends an email reminder. The reminder tracks:
  • stripeOnboardingReminderSentAt — when the last reminder was sent
  • stripeOnboardingReminderClaimedUntil — suppresses reminders until this date

Troubleshooting

IssueSolution
Creator can’t receive payoutsVerify their Stripe account status via GET /creator/id/{id}/stripe/account. If restricted, the creator needs to complete additional verification
Payouts stuck in PENDINGCheck the payouts-sweep cron job is running. Verify the hold period hasn’t been extended
Application fee incorrectVerify STRIPE_PLATFORM_APPLICATION_FEE_BPS in your environment

Privy Wallets

Privy provides embedded wallet infrastructure for both server-side automation and client-side user wallets.

Environment Variables

PRIVY_APP_ID=your_privy_app_id
PRIVY_APP_SECRET=your_privy_app_secret

Server Wallets (Backend / Agents)

Server wallets are platform-managed — your backend controls signing. Used for automated x402 payments, reward minting, and agent-initiated transfers.
import { PrivyClient } from '@privy-io/server-auth';

const privy = new PrivyClient(
  process.env.PRIVY_APP_ID,
  process.env.PRIVY_APP_SECRET
);

const wallet = await privy.wallets().create({
  chain_type: 'ethereum'
});

Embedded Wallets (Frontend)

Embedded wallets are auto-created for end users on first login, giving them a non-custodial wallet without seed phrase management.
import { PrivyProvider } from '@privy-io/react-auth';

function App() {
  return (
    <PrivyProvider
      appId={process.env.PRIVY_APP_ID}
      config={{
        loginMethods: ['email', 'telegram', 'google'],
        embeddedWallets: {
          ethereum: { createOnLogin: 'all-users' }
        },
        supportedChains: [base]
      }}
    >
      <YourApp />
    </PrivyProvider>
  );
}

User Linking

Privy users are linked to Podium users via the PrivyUser model:
Privy DIDPodium User
did:privy:abc123clxyz1234567890
Resolve the link with:
GET /api/v1/user/privy/{privyId}/profile

Funding Embedded Wallets

Users can fund their embedded wallets via:
  • Apple Pay / Google Pay (through Privy’s fiat on-ramp)
  • Coinbase transfer
  • Direct USDC transfer from any wallet
  • Points-to-USDC conversion (if enabled)

Troubleshooting

IssueSolution
Wallet not created on loginVerify createOnLogin: 'all-users' in Privy config
Transaction failuresCheck the Privy dashboard for transaction logs. Verify sufficient gas/USDC balance
User can’t link external walletEnsure walletConnectors includes the desired provider

Stream Chat

Podium integrates Stream Chat for real-time messaging between users and creators.

Environment Variables

STREAM_ACCESS_KEY=your_stream_key
STREAM_ACCESS_SECRET=your_stream_secret

Setup

1

Generate tokens

Server generates Stream Chat tokens:
POST /api/v1/user/{id}/chat
or for creator-initiated chats:
POST /api/v1/creator/id/{creatorId}/chat
2

Client connection

Connect using the Stream Chat SDK with the generated token:
import { StreamChat } from 'stream-chat';

const client = StreamChat.getInstance(STREAM_ACCESS_KEY);
await client.connectUser(
  { id: userId, name: username },
  streamChatToken
);
3

Channel creation

Channels are created per user-creator pair. The server creates and manages channels to enforce access control.

Request Body

{
  "requestorId": "clcreator_abc",
  "requestorType": "Creator",
  "userId": "clxyz1234567890"
}
FieldTypeRequiredDescription
requestorIdstringYesCUID of the initiating party
requestorTypeenumYes"User" or "Creator"
userIdstringNoTarget user (for creator-initiated chats)

Shippo Shipping

Podium uses Shippo for shipping rate calculation and label generation.

OAuth Installation

1

Initiate OAuth

GET /api/v1/shippo/install
2

Callback

Shippo redirects to /api/v1/shippo/callback with an access token. Stored in ShippoAccount linked to the creator.

Get Shipping Quotes

POST /api/v1/order/{id}/shipping/quote
Provide the recipient’s shipping address in the request body to receive available shipping rates from Shippo based on the order’s weight, dimensions, and destination:
{
  "rates": [
    {
      "carrier": "USPS",
      "service": "Priority Mail",
      "amount": 7.99,
      "currency": "USD",
      "estimatedDays": 3,
      "rateId": "rate_abc123"
    },
    {
      "carrier": "UPS",
      "service": "Ground",
      "amount": 12.50,
      "currency": "USD",
      "estimatedDays": 5,
      "rateId": "rate_def456"
    }
  ]
}

Generate Shipping Label

GET /api/v1/creator/id/{creatorId}/order/{orderId}/shipping/label
Retrieves the shipping label for an order via Shippo. Returns the label URL and tracking number.

Flat Rate Fallback

Creators can set a flatShippingRate (in cents) on their profile as a default when Shippo quotes aren’t available or desired:
PATCH /api/v1/creator/id/{creatorId}
{ "flatShippingRate": 799 }

Troubleshooting

IssueSolution
No shipping quotesVerify the order has valid shipping addresses and product weights
Label generation failsCheck the creator’s Shippo account is connected and has a valid payment method
Rates seem highReview product weight/dimension data — incorrect values inflate shipping costs
ServiceDocumentation
Shopify APIAdmin API, webhooks, OAuth
Stripe ConnectExpress accounts, payouts, platform fees
PrivyEmbedded wallets, server wallets, auth
Stream ChatReal-time messaging SDK
ShippoShipping rates, labels, tracking