Skip to main content
Stripe is Gumroad’s primary payment processor, handling card payments, bank transfers, and seller payouts through Stripe Connect.

Stripe Connect Overview

Gumroad uses Stripe Connect Custom accounts to process payments and manage seller payouts. This provides:
  • Direct card payment processing
  • Automated compliance and KYC verification
  • Native bank account payouts in 40+ countries
  • Fraud protection with Stripe Radar

Supported Countries

Stripe Connect is available for sellers in these countries:
COUNTRIES_SUPPORTED_BY_STRIPE_CONNECT = [
  "Australia", "Austria", "Belgium", "Brazil", "Bulgaria", "Canada", "Croatia",
  "Cyprus", "Czechia", "Denmark", "Estonia", "Finland", "France",
  "Germany", "Gibraltar", "Greece", "Hong Kong", "Hungary", "Ireland", "Italy",
  "Japan", "Latvia", "Liechtenstein", "Lithuania", "Luxembourg",
  "Malta", "Netherlands", "New Zealand", "Norway", "Poland", "Portugal",
  "Romania", "Singapore", "Slovakia", "Slovenia", "Spain", "Sweden", "Switzerland",
  "United Arab Emirates", "United Kingdom", "United States"
]

Configuration

Environment Variables

Configure Stripe in your .env file:
# Stripe API Configuration
STRIPE_API_KEY=sk_live_...
STRIPE_CONNECT_CLIENT_ID=ca_...
STRIPE_PLATFORM_ACCOUNT_ID=acct_...
STRIPE_PUBLIC_KEY_PROD=pk_live_...
STRIPE_PUBLIC_KEY_TEST=pk_test_...

# Webhook Secrets
STRIPE__ENDPOINT_SECRET=whsec_...
STRIPE_CONNECT__ENDPOINT_SECRET=whsec_...

Gemfile Dependencies

gem "stripe", "~> 12.0"
gem "omniauth-stripe-connect"

Creating Stripe Accounts

Gumroad automatically creates Stripe Connect accounts when sellers:
  1. Provide compliance information (legal name, address, tax ID)
  2. Connect a bank account
  3. Agree to Terms of Service

Account Creation Flow

# app/business/payments/merchant_registration/implementations/stripe/
StripeMerchantAccountManager.create_account(
  user,
  passphrase: GlobalConfig.get("STRONGBOX_GENERAL_PASSWORD")
)
The system creates a Stripe Custom Connect account with:
  • Account Type: custom
  • Capabilities: ["card_payments", "transfers"]
  • Currency: Based on country (USD, EUR, GBP, etc.)
  • Payout Schedule: Manual (controlled by Gumroad)
Stripe payouts are set to manual mode. Gumroad controls when payouts are sent based on the seller’s configured payout frequency.

Bank Account Connection

Sellers connect bank accounts through the payment settings page:
# app/controllers/settings/payments_controller.rb
def update
  # Create bank account record
  return unless update_payout_method
  
  # Create Stripe merchant account if eligible
  if current_seller.active_bank_account && 
     current_seller.native_payouts_supported?
    StripeMerchantAccountManager.create_account(
      current_seller,
      passphrase: GlobalConfig.get("STRONGBOX_GENERAL_PASSWORD")
    )
  end
end

Supported Bank Account Types

Depending on country, Stripe supports:
  • Standard bank accounts (account + routing number)
  • IBAN accounts (Europe)
  • Debit cards (US only, for instant payouts)

Webhook Events

Gumroad listens for these Stripe webhook events:

account.updated

Handles changes to Stripe account status:
  • Verification status updates
  • Compliance requirements
  • Capability changes
  • Payout enablement
def handle_stripe_event_account_updated(stripe_event)
  stripe_account = stripe_event["data"]["object"]
  
  # Check compliance requirements
  requirements = stripe_account["requirements"] || {}
  fields_needed = requirements["currently_due"] || []
  
  # Update merchant account status
  if stripe_account["payouts_enabled"]
    # Enable payouts
  else
    # Pause payouts, send notification email
  end
end

account.application.deauthorized

Handles when seller disconnects their Stripe account:
def handle_stripe_event_account_deauthorized(stripe_event)
  merchant_account = MerchantAccount.find_by(
    charge_processor_merchant_id: stripe_event["account"]
  )
  
  merchant_account.delete_charge_processor_account!
  
  # Send notification email to seller
  MerchantRegistrationMailer.account_deauthorized_to_user(
    user.id,
    StripeChargeProcessor.charge_processor_id
  ).deliver_later
end

capability.updated

Handles capability status changes (e.g., card_payments, transfers).
If Stripe disables payouts due to verification issues, sellers receive an email with a remediation link to complete verification.

Compliance & Verification

Stripe requires different information based on:
  • Country: Each country has specific requirements
  • Business Type: Individual vs Company
  • Payment Volume: Higher volume triggers additional verification

Required Fields

Individual accounts need:
  • Legal name (first + last name)
  • Date of birth
  • Address
  • Tax ID (SSN, EIN, or country equivalent)
  • Email and phone
Business accounts additionally need:
  • Business name
  • Business address
  • Business tax ID
  • Representative information
  • Ownership details

Field Mapping

Gumroad maps compliance info to Stripe fields:
# app/business/payments/merchant_registration/implementations/stripe/
module StripeUserComplianceInfoFieldMap
  def self.map(stripe_field)
    # Maps Stripe requirement fields to Gumroad compliance fields
    # e.g., "individual.dob.day" -> "birthday"
  end
end

Stripe Radar Fraud Prevention

All charges are processed through Stripe Radar for fraud detection:
# app/business/payments/charging/implementations/stripe/
module StripeChargeRadarProcessor
  # Stripe Radar automatically blocks high-risk charges
end

Disconnecting Stripe

Sellers can disconnect Stripe from Settings > Payments:
# app/controllers/settings/stripe_controller.rb
def disconnect
  StripeMerchantAccountManager.disconnect(user: logged_in_user)
end
Disconnecting Stripe while having active subscriptions or preorders will fail. Sellers must migrate those to PayPal first.

Cross-Border Payouts

Some countries only support Stripe for cross-border payouts (receiving USD):
def supports_stripe_cross_border_payouts?
  # Returns true for countries that can receive USD payouts
  # but cannot process card payments
end
These accounts have limited capabilities and use service_agreement: "recipient" in TOS acceptance.

Testing

In development/test environments:
  • Use Stripe test API keys
  • Non-USD bank accounts are allowed (normally restricted in production)
  • Webhook events can be simulated

Troubleshooting

Payouts Disabled

Cause: Stripe requires additional verification Solution: Seller receives email with remediation link
# Access remediation flow
GET /settings/payments/remediation

Invalid Bank Account

Cause: Bank account details are incorrect Solution: Seller receives email to update bank information
ContactingCreatorMailer.invalid_bank_account(user.id).deliver_later

Locked Verification Fields

Cause: Stripe has verified certain fields and they cannot be changed Solution: Only send changed fields in update requests
def get_diff_attributes(current_attributes, last_attributes)
  # Only sends fields that have actually changed
  # to avoid Stripe errors on locked fields
end

Build docs developers (and LLMs) love