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:
- Provide compliance information (legal name, address, tax ID)
- Connect a bank account
- 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