A Cloudflare Worker that adds a standalone PayPal checkout flow to a Webround store, without Stripe Connect and without merchant-of-record setup.
Stripe Connect in standard mode doesn't support PayPal unless you're a merchant of record. This Worker sits between your Webround store and PayPal, handling the full checkout flow independently.
Customer clicks PayPal → Worker validates cart via Webround API
→ Creates Webround order (status: created)
→ Creates PayPal order
→ Returns PayPal order ID to frontend
Customer approves → Frontend captures the PayPal order
PayPal sends webhook → Worker verifies HMAC signature
→ Patches Webround order to paid
→ Webround fires order.paid to your integrations
src/
index.ts # Worker entry point — routes and orchestration
paypal.ts # PayPal API (token, create order, capture, webhook verify)
webround.ts # Webround API (cart validation, order creation, status update)
types.ts # Shared types and Env interface
PayPalButton.tsx # Webround custom React component (drop into the IDE)
schema.sql # D1 table to map PayPal order IDs → Webround order IDs
wrangler.jsonc # Cloudflare Worker config (staging + production)
1. Create the D1 database
wrangler d1 create my-store-paypal
wrangler d1 execute my-store-paypal --file=schema.sql2. Set secrets
# secrets-staging.json and secrets-prod.json — never commit these
{
"WR_API_KEY": "...",
"PAYPAL_CLIENT_ID": "...",
"PAYPAL_SECRET": "..."
}3. Update wrangler.jsonc with your store ID, user ID, database IDs, and webhook IDs.
4. Deploy
make deploy-staging
make deploy-prodDrop PayPalButton.tsx into the Webround built-in React IDE and add it to your cart page. Install @paypal/react-paypal-js from the IDE package manager.
Stripe and PayPal calculate order totals differently on multi-quantity lines with decimal prices. This Worker aligns PayPal's calculation to Stripe's method to avoid silent discrepancies in accounting exports. See the blog post for the full explanation.