Skip to content

PayPal integration into purchases-js as payment gateway#815

Draft
rogersole wants to merge 5 commits intomainfrom
paypal-integration-into-purchases-js-as-payment-gateway
Draft

PayPal integration into purchases-js as payment gateway#815
rogersole wants to merge 5 commits intomainfrom
paypal-integration-into-purchases-js-as-payment-gateway

Conversation

@rogersole
Copy link
Copy Markdown
Contributor

@rogersole rogersole commented Mar 23, 2026

Summary

  • Adds PayPal as a payment gateway (like Stripe), not an external billing integration (like Paddle)
  • PayPal availability is determined by the backend returning paypal_gateway_params in checkout/prepare and checkout/start responses — no separate API key prefix
  • Uses a popup window for PayPal's approval flow, with backend operation status polling after the popup closes
  • Aligns with backend changes in https://github.com/RevenueCat/khepri/pull/18770

Changes

New files:

  • src/paypal/paypal-service.ts — Popup + polling logic for PayPal approval flow
  • src/tests/paypal/paypal-service.test.ts — Unit tests for PayPalService

Modified files:

  • src/networking/responses/checkout-start-response.ts — Added PayPalGatewayParams and paypal_gateway_params field to WebBillingCheckoutStartResponse
  • src/networking/responses/checkout-prepare-response.ts — Updated CheckoutPreparePayPalGatewayParams from { client_access_token } to { is_sandbox }
  • src/helpers/purchase-operation-helper.ts — Exposed getBackend() and currentOperationSessionId for PayPal flow
  • src/ui/purchases-ui.svelte — Extracts and passes paypal_gateway_params from checkout start response
  • src/ui/purchases-ui-inner.svelte — Passes paypalGatewayParams through to payment entry page
  • src/ui/pages/payment-entry-page.svelte — Renders "Pay with PayPal" button when paypal_gateway_params is present
  • src/helpers/api-key-helper.ts — Removed pypl_ API key prefix and isPayPalApiKey()
  • src/main.ts — Removed separate PayPal routing and performPayPalPurchase()

Deleted files:

  • src/ui/paypal-purchases-ui.svelte — Standalone PayPal UI (replaced by integration into existing checkout)
  • src/ui/paypal-purchases-ui-inner.svelte — Standalone PayPal inner UI

Backend contract (from Khepri PR)

POST /checkout/start returns paypal_gateway_params as an optional field alongside existing Stripe fields:

{
  "operation_session_id": "...",
  "gateway_params": { ... },
  "stripe_billing_params": { ... },
  "paypal_gateway_params": {
    "order_id": "...",
    "approval_url": "https://www.paypal.com/checkoutnow?token=...",
    "is_sandbox": true
  }
}

POST /checkout/prepare returns:

{
  "stripe_gateway_params": { ... },
  "paypal_gateway_params": {
    "is_sandbox": true
  }
}

Test plan

  • All 554 tests pass
  • TypeScript typecheck passes (both tsconfig.json and tsconfig.test.json)
  • Svelte check passes
  • ESLint passes
  • End-to-end test with Khepri PR in sandbox

🤖 Generated with Claude Code

rogersole and others added 5 commits March 23, 2026 11:11
Integrate PayPal as a new checkout provider following the same pattern
as the existing Paddle integration. Uses a popup window approach for
PayPal's approval flow, with backend operation status polling after
the popup closes.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Attach rejection handlers before advancing fake timers so the async
rejections from polling intervals are caught before vitest flags them
as unhandled.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Remove pypl_ API key prefix and separate PayPal routing
- Remove standalone PayPal UI components (paypal-purchases-ui.svelte)
- Remove PayPalCheckoutStartResponse; add paypal_gateway_params to
  WebBillingCheckoutStartResponse instead
- Update CheckoutPreparePayPalGatewayParams to { is_sandbox } per
  backend contract
- Simplify PayPalService to only handle popup + polling (remove
  startCheckout, which is handled by PurchaseOperationHelper)
- Add PayPal button to existing payment-entry-page when
  paypal_gateway_params is present in checkout/start response
- Wire paypal_gateway_params through purchases-ui → inner → page
- Expose getBackend() and currentOperationSessionId on
  PurchaseOperationHelper for PayPal flow

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Defaults to null so stories and other consumers that don't need
PayPal don't have to pass it.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@RevenueCat-Danger-Bot
Copy link
Copy Markdown

1 Error
🚫 Label the PR using one of the change type labels. If you are not sure which label to use, choose pr:other.
Label Description
pr:feat A new feature. Use along with pr:breaking to force a major release.
pr:fix A bug fix. Use along with pr:force_minor to force a minor release.
pr:other Other changes. Catch-all for anything that doesn't fit the above categories. Releases that only contain this label will not be released. Use along with pr:force_patch, or pr:force_minor to force a patch or minor release.
pr:RevenueCatUI Use along any other tag to mark a PR that only contains RevenueCatUI changes
pr:next_release Preparing a new release
pr:dependencies Updating a dependency
pr:phc_dependencies Updating purchases-hybrid-common dependency
pr:changelog_ignore The PR will not be included in the changelog. This label doesn't determine the type of bump of the version and must be combined with pr:feat, pr:fix or pr:other.

Generated by 🚫 Danger

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants