# Bridge Payments - Full Developer Context > Universal payment backend for Pubflow ecosystem > Built with Bun, Hono, Kysely ORM - Supports Stripe, PayPal, Authorize.net, and more on request ## Table of Contents 1. Overview & Architecture 2. Installation & Configuration 3. API Reference 4. Organizations Management 5. Payment Providers 6. Integration Examples (Production-Ready) 7. Code Examples 8. Database Schema 9. Webhook System 10. Security & Best Practices --- ## 1. Overview & Architecture ### What is Bridge Payments? Bridge Payments is a universal payment processing backend built on the Pubflow platform. It provides: - **Universal Payment System**: Extensible architecture supporting multiple providers - **Currently Supported**: Stripe, PayPal, Authorize.net - **Available on Request**: Square, Braintree, Adyen, Razorpay, Mercado Pago, 2Checkout, Klarna, and more - Seamless Flowless authentication integration - Guest checkout support - Subscription management - Webhook processing - Multi-database support ### Requesting New Payment Providers Bridge Payments can integrate additional payment providers based on demand. **How to Request:** Use the Pubflow Request Center at https://www.pubflow.com/requests (recommended) or email support@pubflow.com **Popular Providers Available:** - Square, Braintree, Adyen, Razorpay, Mercado Pago, 2Checkout, Klarna, Mollie, Checkout.com, Worldpay, and more **Integration Timeline:** 2-4 weeks depending on provider complexity **Learn more:** /providers/request-new.html ### Architecture ``` Application Layer (React, Next.js, React Native) | v +---------------+ +------------------+ | Flowless |<------->| Bridge Payments | | (Auth Server) | | (Payment Server) | +---------------+ +------------------+ | +-----------------+-----------------+ | | | v v v +---------+ +---------+ +-------------+ | Stripe | | PayPal | |Authorize.net| +---------+ +---------+ +-------------+ ``` ### Core Components 1. **Authentication Bridge**: Validates sessions with Flowless 2. **Payment Processing Engine**: Handles payment intents and confirmations 3. **Provider Adapters**: Stripe, PayPal, Authorize.net integrations 4. **Database Layer**: Multi-database support with Kysely ORM 5. **Webhook System**: Provider webhooks + external webhooks 6. **Cache Layer**: Hybrid caching—optional Redis (`REDIS_URL`) with in-memory LRU fallback ### Technology Stack - **Runtime**: Bun (fast JavaScript runtime) - **Framework**: Hono (lightweight web framework) - **ORM**: Kysely (type-safe SQL query builder) - **Validation**: Zod (TypeScript schema validation) - **Cache**: Optional Redis with LRU fallback (SmartCache) - **Payment SDKs**: stripe, @paypal/sdk-client, authorizenet --- ## 2. Installation & Configuration ### Creating Instance 1. Log in to Pubflow Platform (pubflow.com) 2. Navigate to Instances → Create New Instance 3. Select Bridge Payments 4. Choose deployment region 5. Instance deployed automatically ### Environment Variables #### Core Configuration ```bash # Database (required) DATABASE_URL=postgresql://user:password@host:5432/database # Flowless Integration (required) FLOWLESS_API_URL=https://your-flowless.pubflow.com BRIDGE_VALIDATION_SECRET=shared_secret_with_flowless # Timeouts (optional) AUTH_TIMEOUT=30000 REQUEST_TIMEOUT=60000 ``` #### Payment Providers ```bash # Stripe STRIPE_SECRET_KEY=sk_test_... STRIPE_PUBLISHABLE_KEY=pk_test_... STRIPE_WEBHOOK_SECRET=whsec_... # PayPal PAYPAL_CLIENT_ID=your_client_id PAYPAL_CLIENT_SECRET=your_client_secret PAYPAL_MODE=sandbox # or 'live' PAYPAL_WEBHOOK_ID=your_webhook_id # Authorize.net AUTHORIZE_NET_API_LOGIN_ID=your_api_login_id AUTHORIZE_NET_TRANSACTION_KEY=your_transaction_key AUTHORIZE_NET_MODE=sandbox # or 'production' AUTHORIZE_NET_SIGNATURE_KEY=your_signature_key ``` #### Guest Checkout ```bash GUEST_CHECKOUT_ENABLED=true GUEST_REQUIRE_EMAIL=true GUEST_TOKEN_EXPIRATION=3600 ``` #### Response Format ```bash ROW_MODE=false # true for object format with ID as key ``` #### External Webhooks ```bash EXTERNAL_WEBHOOKS_ENABLED=true WEBHOOK_DEBUG_MODE=true # Configure multiple webhooks (numbered) WEBHOOK_1_NAME=discord_alerts WEBHOOK_1_URL=https://discord.com/api/webhooks/... WEBHOOK_1_EVENTS=payment.failed,subscription.cancelled WEBHOOK_1_SECRET=optional_secret WEBHOOK_2_NAME=slack_notifications WEBHOOK_2_URL=https://hooks.slack.com/services/... WEBHOOK_2_EVENTS=payment.success,subscription.created ``` --- ## 3. API Reference ### Base URL ``` https://your-instance.pubflow.com/bridge-payment ``` ### Authentication Include one of: - Header: `X-Session-ID: ` (recommended) - Header: `Authorization: Bearer ` - Query: `?session_id=` - Guest: Provide `guest_data` in body (no auth) ### Payments API #### Create Payment Intent ```http POST /bridge-payment/payments/intents Content-Type: application/json X-Session-ID: { "subtotal_cents": 1800, "tax_cents": 200, "total_cents": 2000, "currency": "USD", "concept": "Premium Subscription", "description": "Monthly premium plan", "provider_id": "stripe", "setup_future_usage": "off_session" } ``` Response: ```json { "id": "pay_1234567890", "provider_payment_id": "pi_stripe_abc123", "client_secret": "pi_stripe_abc123_secret_xyz", "status": "created", "total_cents": 2000, "currency": "USD" } ``` #### Update Payment Intent ```http PUT /bridge-payment/payments/intents/:id ``` #### Confirm Payment ```http POST /bridge-payment/payments/confirm/:id ``` #### Sync Payment Status ```http POST /bridge-payment/payments/sync/:provider_payment_id ``` #### Get Payment ```http GET /bridge-payment/payments/:id ``` #### List Payments ```http GET /bridge-payment/payments ``` #### List Guest Payments ```http GET /bridge-payment/payments/guest/:email ``` --- ## 4. Organizations Management ### Overview Organizations API provides multi-tenant support with role-based access control. Perfect for: - Team subscriptions - Business accounts - Multi-user payment management - Delegated billing access ### Roles & Permissions | Role | Permissions | |------|-------------| | **owner** | Full control: create, update, delete org, manage members | | **admin** | Full access except delete org and manage members | | **billing** | Financial operations only | | **member** | Read-only access | ### Organizations CRUD #### Create Organization ```http POST /bridge-payment/organizations Content-Type: application/json X-Session-ID: { "name": "Acme Corporation", "business_email": "billing@acme.com", "business_phone": "+1-555-0123", "tax_id": "12-3456789", "address": "123 Main St, San Francisco, CA 94105" } ``` Response: ```json { "success": true, "data": { "id": "org_abc123", "name": "Acme Corporation", "owner_user_id": "user_123", "business_email": "billing@acme.com", "business_phone": "+1-555-0123", "tax_id": "12-3456789", "address": "123 Main St, San Francisco, CA 94105", "created_at": "2025-01-15T10:30:00Z", "updated_at": "2025-01-15T10:30:00Z" } } ``` #### List Organizations ```http GET /bridge-payment/organizations?page=1&limit=20 X-Session-ID: ``` #### Get Organization ```http GET /bridge-payment/organizations/:id X-Session-ID: ``` #### Update Organization (Owner Only) ```http PUT /bridge-payment/organizations/:id Content-Type: application/json X-Session-ID: { "name": "Acme Corporation Ltd", "business_email": "accounting@acme.com" } ``` #### Delete Organization (Owner Only) ```http DELETE /bridge-payment/organizations/:id X-Session-ID: ``` ### Member Management #### List Members ```http GET /bridge-payment/organizations/:id/members?page=1&limit=20&role=admin X-Session-ID: ``` #### Add Member (Owner Only) ```http POST /bridge-payment/organizations/:id/members Content-Type: application/json X-Session-ID: { "email": "newmember@acme.com", "role": "billing" } ``` **Note:** User must already exist in the system. They are automatically added to the organization. #### Update Member Role (Owner Only) ```http PUT /bridge-payment/organizations/:id/members/:memberId Content-Type: application/json X-Session-ID: { "role": "admin" } ``` #### Remove Member (Owner Only) ```http DELETE /bridge-payment/organizations/:id/members/:memberId X-Session-ID: ``` #### Leave Organization ```http POST /bridge-payment/organizations/:id/leave X-Session-ID: ``` **Note:** Owner cannot leave. They must delete the organization instead. ### Security Rules ✅ **Authentication Required:** All endpoints require authenticated users (no guest access) ✅ **Owner-Only Operations:** Update org, delete org, manage members ✅ **Role Restrictions:** Cannot create multiple owners, cannot change owner role ✅ **Self-Protection:** Cannot change your own role ✅ **Cache Invalidation:** Automatic cache refresh after changes ### Use Cases **Team Subscriptions:** ```typescript // 1. Create organization const org = await createOrganization({ name: "Acme Corp" }); // 2. Add billing manager await addMember(org.id, { email: "billing@acme.com", role: "billing" }); // 3. Create subscription with organization_id const subscription = await createSubscription({ organization_id: org.id, product_id: "team_plan", payment_method_id: "pm_123" }); ``` **Delegated Access:** ```typescript // Add admin who can manage subscriptions but not delete org await addMember(org.id, { email: "admin@acme.com", role: "admin" }); // Add read-only member await addMember(org.id, { email: "viewer@acme.com", role: "member" }); ``` --- ## 5. Payment Providers ### Stripe **Features:** - Credit cards (Visa, Mastercard, Amex, Discover) - Apple Pay, Google Pay - 3D Secure (SCA compliance) - Payment Elements (recommended) **Setup:** 1. Get API keys from dashboard.stripe.com/apikeys 2. Add to environment: STRIPE_SECRET_KEY, STRIPE_PUBLISHABLE_KEY 3. Configure webhook: dashboard.stripe.com/webhooks 4. Add STRIPE_WEBHOOK_SECRET **Frontend Integration:** ```typescript import { loadStripe } from '@stripe/stripe-js'; import { Elements, CardElement } from '@stripe/react-stripe-js'; const stripe = await loadStripe('pk_test_...'); // Confirm payment const { error } = await stripe.confirmPayment({ elements, confirmParams: { return_url: `${window.location.origin}/success` } }); ``` ### PayPal **Features:** - PayPal accounts - Venmo - Credit cards (via PayPal) **Setup:** 1. Create app at developer.paypal.com 2. Get Client ID and Secret 3. Add to environment: PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET 4. Set PAYPAL_MODE (sandbox or live) ### Authorize.net **Features:** - Credit cards - Bank accounts (ACH) **Setup:** 1. Get credentials from Authorize.net merchant dashboard 2. Add to environment: AUTHORIZE_NET_API_LOGIN_ID, AUTHORIZE_NET_TRANSACTION_KEY 3. Set AUTHORIZE_NET_MODE (sandbox or production) --- ## 5. Integration Examples (Production-Ready) ::: tip Production-Ready REST API Bridge Payments is a production-ready REST API currently in use across multiple applications. Official SDKs are on the roadmap! The examples below show battle-tested integration patterns using standard HTTP clients (fetch/axios). ::: ### React Native **Direct API Integration:** ```typescript // lib/bridge-payments.ts const BRIDGE_URL = 'https://your-instance.pubflow.com'; export async function createPaymentIntent(data: { total_cents: number; currency: string; sessionId: string; }) { const response = await fetch(`${BRIDGE_URL}/bridge-payment/payments/intents`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Session-ID': data.sessionId }, body: JSON.stringify({ total_cents: data.total_cents, currency: data.currency }) }); if (!response.ok) { throw new Error('Failed to create payment intent'); } return response.json(); } // Usage const intent = await createPaymentIntent({ total_cents: 2000, currency: 'USD', sessionId: userSession.id }); ``` ### Next.js **Direct API Integration:** ```typescript // app/api/create-payment/route.ts export async function POST(request: Request) { const { amount, sessionId } = await request.json(); const response = await fetch( 'https://your-instance.pubflow.com/bridge-payment/payments/intents', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Session-ID': sessionId }, body: JSON.stringify({ total_cents: amount, currency: 'USD' }) } ); if (!response.ok) { return Response.json({ error: 'Payment failed' }, { status: 400 }); } return response.json(); } ``` ### React **Direct API Integration:** ```typescript // hooks/useBridgePayments.ts import { useState } from 'react'; const BRIDGE_URL = 'https://your-instance.pubflow.com'; export function useBridgePayments(sessionId: string) { const [loading, setLoading] = useState(false); const createPaymentIntent = async (data: { total_cents: number; currency: string; }) => { setLoading(true); try { const response = await fetch(`${BRIDGE_URL}/bridge-payment/payments/intents`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Session-ID': sessionId }, body: JSON.stringify(data) }); if (!response.ok) throw new Error('Payment failed'); return await response.json(); } finally { setLoading(false); } }; return { createPaymentIntent, loading }; } ``` --- ## 6. Code Examples ### Complete Payment Flow (React Native) ```typescript import { useState } from 'react'; import { useStripe } from '@stripe/stripe-react-native'; const BRIDGE_URL = process.env.EXPO_PUBLIC_BRIDGE_BASE_PAYMENT_URL; function CheckoutScreen({ sessionId }: { sessionId: string }) { const stripe = useStripe(); const [loading, setLoading] = useState(false); const handlePayment = async () => { setLoading(true); try { // 1. Create payment intent const intentResponse = await fetch(`${BRIDGE_URL}/bridge-payment/payments/intents`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Session-ID': sessionId }, body: JSON.stringify({ subtotal_cents: 1800, tax_cents: 200, total_cents: 2000, currency: 'USD', concept: 'Premium Subscription', provider_id: 'stripe' }) }); const intent = await intentResponse.json(); // 2. Confirm payment with Stripe const { error, paymentIntent } = await stripe.confirmPayment( intent.data.client_secret, { paymentMethodType: 'Card' } ); if (error) { console.error('Payment failed:', error.message); return; } // 3. Sync status with backend const syncResponse = await fetch( `${BRIDGE_URL}/bridge-payment/payments/${paymentIntent.id}/sync`, { method: 'POST', headers: { 'X-Session-ID': sessionId } } ); const payment = await syncResponse.json(); if (payment.data.status === 'succeeded') { console.log('Payment successful!'); } } catch (error) { console.error('Error:', error); } finally { setLoading(false); } }; return ( ); } export default function DonatePage() { return ( ); } ``` ```typescript // app/api/create-donation/route.ts export async function POST(request: Request) { const { amount, guest_data } = await request.json(); const response = await fetch( `${process.env.BRIDGE_PAYMENTS_URL}/bridge-payment/payments/intents`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ total_cents: amount, currency: 'USD', concept: 'Donation', provider_id: 'stripe', guest_data }) } ); return response.json(); } ``` ### Saved Payment Methods ```typescript // Save payment method during payment const intent = await client.createPaymentIntent({ total_cents: 2000, currency: 'USD', concept: 'First Purchase', setup_future_usage: 'off_session' // Save for future use }); // List saved payment methods const methods = await client.getPaymentMethods(); // Use saved payment method const payment = await client.createPaymentIntent({ total_cents: 2000, currency: 'USD', concept: 'Subscription Renewal', payment_method_id: methods[0].id // Use saved method }); ``` ### Subscriptions ```typescript // Create subscription const subscription = await fetch( `${baseUrl}/bridge-payment/subscriptions`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Session-ID': sessionId }, body: JSON.stringify({ customer_id: 'cust_123', product_id: 'prod_premium', payment_method_id: 'pm_saved_card', trial_days: 14, concept: 'Premium Monthly Plan' }) } ); // Cancel subscription await fetch( `${baseUrl}/bridge-payment/subscriptions/${subscriptionId}/cancel`, { method: 'POST', headers: { 'X-Session-ID': sessionId } } ); ``` --- ## 7. Database Schema ### Tables #### provider_payments Stores payment transactions. ```sql CREATE TABLE provider_payments ( id VARCHAR(255) PRIMARY KEY, user_id VARCHAR(255), organization_id VARCHAR(255), customer_id VARCHAR(255), payment_method_id VARCHAR(255), provider_id VARCHAR(50) NOT NULL, provider_payment_id VARCHAR(255), provider_intent_id VARCHAR(255), client_secret TEXT, status VARCHAR(50) DEFAULT 'created', subtotal_cents INTEGER NOT NULL, tax_cents INTEGER DEFAULT 0, discount_cents INTEGER DEFAULT 0, total_cents INTEGER NOT NULL, currency VARCHAR(3) NOT NULL, description TEXT, concept VARCHAR(255), reference_code VARCHAR(255), category VARCHAR(100), tags TEXT, metadata JSON, is_guest_payment BOOLEAN DEFAULT FALSE, guest_email VARCHAR(255), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, completed_at TIMESTAMP ); ``` #### provider_payment_methods Stores saved payment methods. ```sql CREATE TABLE provider_payment_methods ( id VARCHAR(255) PRIMARY KEY, user_id VARCHAR(255), organization_id VARCHAR(255), customer_id VARCHAR(255), provider_id VARCHAR(50) NOT NULL, provider_payment_method_id VARCHAR(255), type VARCHAR(50), card_brand VARCHAR(50), card_last_four VARCHAR(4), card_exp_month VARCHAR(2), card_exp_year VARCHAR(4), billing_address_id VARCHAR(255), is_default BOOLEAN DEFAULT FALSE, is_guest_method BOOLEAN DEFAULT FALSE, guest_email VARCHAR(255), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` #### provider_customers Stores customer information. ```sql CREATE TABLE provider_customers ( id VARCHAR(255) PRIMARY KEY, user_id VARCHAR(255), organization_id VARCHAR(255), provider_id VARCHAR(50) NOT NULL, provider_customer_id VARCHAR(255), email VARCHAR(255) NOT NULL, name VARCHAR(255), first_name VARCHAR(255), last_name VARCHAR(255), phone VARCHAR(50), is_guest BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` #### provider_subscriptions Stores subscription information. ```sql CREATE TABLE provider_subscriptions ( id VARCHAR(255) PRIMARY KEY, customer_id VARCHAR(255) NOT NULL, product_id VARCHAR(255), payment_method_id VARCHAR(255), provider_id VARCHAR(50) NOT NULL, provider_subscription_id VARCHAR(255), status VARCHAR(50) DEFAULT 'active', current_period_start TIMESTAMP, current_period_end TIMESTAMP, cancel_at_period_end BOOLEAN DEFAULT FALSE, trial_end TIMESTAMP, subtotal_cents INTEGER NOT NULL, tax_cents INTEGER DEFAULT 0, discount_cents INTEGER DEFAULT 0, total_cents INTEGER NOT NULL, currency VARCHAR(3) NOT NULL, billing_interval VARCHAR(50) NOT NULL, interval_multiplier INTEGER DEFAULT 1, next_billing_date TIMESTAMP, billing_status VARCHAR(50) DEFAULT 'active', concept VARCHAR(255), is_guest_subscription BOOLEAN DEFAULT FALSE, guest_email VARCHAR(255), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` --- ## 8. Webhook System ### Provider Webhooks Bridge Payments automatically processes webhooks from payment providers. **Stripe Webhook Setup:** 1. Go to dashboard.stripe.com/webhooks 2. Add endpoint: `https://your-instance.pubflow.com/bridge-payment/webhooks/stripe` 3. Select events: payment_intent.*, customer.subscription.*, invoice.* 4. Copy webhook secret to STRIPE_WEBHOOK_SECRET **PayPal Webhook Setup:** 1. Go to developer.paypal.com/dashboard 2. Add webhook: `https://your-instance.pubflow.com/bridge-payment/webhooks/paypal` 3. Select events: PAYMENT.*, BILLING.SUBSCRIPTION.* 4. Copy webhook ID to PAYPAL_WEBHOOK_ID ### External Webhooks Send payment events to your own services. **Configuration:** ```bash EXTERNAL_WEBHOOKS_ENABLED=true # Discord notifications WEBHOOK_1_NAME=discord_alerts WEBHOOK_1_URL=https://discord.com/api/webhooks/123/abc WEBHOOK_1_EVENTS=payment.failed,subscription.cancelled # Internal API WEBHOOK_2_NAME=internal_api WEBHOOK_2_URL=https://api.yourcompany.com/webhooks/payments WEBHOOK_2_SECRET=your_secret WEBHOOK_2_EVENTS=* WEBHOOK_2_HEADERS={"Authorization":"Bearer token"} ``` **Event Types:** - payment_intent.succeeded - payment_intent.payment_failed - customer.subscription.created - customer.subscription.updated - customer.subscription.deleted - invoice.payment_failed **Payload Example:** ```json { "event": "payment_intent.succeeded", "data": { "payment_id": "pi_123", "customer_id": "cus_456", "timestamp": "2025-01-15T10:30:00Z", "payment": { "id": "pay_bridge_123", "amount_cents": 5000, "currency": "USD", "status": "succeeded" }, "customer": { "email": "john@example.com", "name": "John Doe" } } } ``` --- ## 9. Security & Best Practices ### Authentication - Always use X-Session-ID header for authenticated requests - Validate sessions with Flowless before processing payments - Use guest_data only for guest checkout flows ### Client Secrets - Client secrets are automatically deleted after 24 hours - Never log or store client secrets - Use immediately on frontend for payment confirmation ### Payment Methods - Use token-based payment methods in production - Never store raw card data - Implement PCI compliance if handling direct card data ### Error Handling - Always sync payment status after frontend confirmation - Handle webhook retries gracefully - Log all payment errors for debugging ### Testing - Use Stripe test mode: sk_test_... - Use PayPal sandbox mode - Test card: 4242 4242 4242 4242 (Stripe) ### Rate Limiting - Default: 100 requests per minute - Configure with RATE_LIMIT environment variable - Implement exponential backoff for retries ### CORS - Configure CORS_ORIGINS for frontend domains - Use specific origins, avoid wildcards in production --- ## Community Contributions ::: info Help Build the Ecosystem Bridge Payments is a production-ready REST API. Official client libraries are on the roadmap! We welcome and encourage community contributions for client libraries across different platforms. ::: ### Why Create a Client Library? While Bridge Payments can be consumed directly via REST API, client libraries can provide: - ✅ **Type Safety** - TypeScript/typed interfaces for API responses - ✅ **Developer Experience** - Simplified API with helper methods - ✅ **Error Handling** - Consistent error handling and retries - ✅ **Authentication** - Automatic session/token management - ✅ **Validation** - Request validation before sending to API ### Suggested Client Library Structure For community-contributed client libraries, we recommend: ``` bridge-payments-{platform}/ |-- src/ | |-- client/ | | |-- BridgePaymentClient.{ext} | | +-- types.{ext} | |-- api/ | | |-- payments.{ext} | | |-- payment-methods.{ext} | | |-- customers.{ext} | | +-- subscriptions.{ext} | |-- utils/ | | |-- auth.{ext} | | +-- formatting.{ext} | +-- index.{ext} |-- tests/ |-- docs/ |-- README.md +-- package.json ``` **Recommended Package Names:** - `bridge-payments-vue` (Vue.js) - `bridge-payments-angular` (Angular) - `bridge-payments-svelte` (Svelte) - `bridge_payments_flutter` (Flutter) - `BridgePaymentsSwift` (Swift/iOS) - `bridge-payments-kotlin` (Kotlin/Android) - `bridge-payments-python` (Python) - `bridge-payments-go` (Go) **Core Features to Include:** - Type-safe API client with full TypeScript support - Automatic authentication handling (session/token) - Error handling with retries and exponential backoff - Request/response validation - Comprehensive documentation and examples --- For complete documentation, visit: https://bridge-payments-docs.pubflow.com