Skip to content

feat: integrate PayPal checkout in Commera - #158

Draft
ishad05 wants to merge 11 commits into
bwhtech:developfrom
ishad05:feat/paypal-commera
Draft

ishad05 wants to merge 11 commits into
bwhtech:developfrom
ishad05:feat/paypal-commera

Conversation

@ishad05

@ishad05 ishad05 commented Sep 23, 2026 •

Copy link
Copy Markdown

Summary

Commera no longer names any payment gateway in code. Every gateway comes from bwh_payments or from another provider app, and this adds the Commera side of PayPal (bwhtech/bwh_payments#7) and Braintree (bwhtech/bwh_payments#23).

Changes

  • Gateway cards come from bwh_payments. Settings → Payments builds a card for every gateway bwh_payments ships, from bwh_payments.api.get_gateways() (name, description, docs link, settings DocType, capabilities). The hard-coded Razorpay, Stripe, Telr and Tabby list is gone, so a new bwh_payments gateway needs no change in Commera. Cards are ordered by name.

  • New hook: commera_payment_gateways, for provider apps outside bwh_payments:

    # provider app's hooks.py
    commera_api_version = [1]
    commera_payment_gateways = ["my_app.gateways.wallet"]
    # each path returns {slug, label, blurb, settings_doctype, docs_url, capabilities}

    Cards appear after bwh_payments' gateways. A card can't replace one of them, an app built for another API version is skipped, and a card that fails or is incomplete is logged and skipped, so one broken app never breaks checkout.

  • reusable_checkout capability. For a gateway whose checkout link stays payable after the shopper closes the tab, such as PayPal and Braintree:

    • a returning shopper is sent back to the open link, or to the confirmation page if it's already paid, after the same checks a new checkout runs
    • the cart is kept while that payment is pending, so a late webhook can still place the order
  • Currency-aware checkout. Checkout only offers gateways that can take the cart's currency, and the server refuses the others.

  • Return links keep reference_id. Redirects from /en-US/, /en-GB/, /account/... and /cart now forward the query string, so shoppers with a US or UK English browser no longer see "could not confirm" after paying.

  • Webhook orders belong to the shopper. A gateway order placed by a webhook (which runs as Administrator) is owned by the cart's shopper, so it shows in their account. COD orders are unchanged.

commera_payment_gateways is a new hook, so the plugin reference needs a page for it.

Testing

  • Whole Commera suite: 965 tests. 14 fail, and none come from this PR: freezegun isn't installed on this bench, two test_sdk carrier tests fail on develop too, and develop still has tests written for an older bwh_payments (test_payment_hooks, plus three refund and fee tests).
  • With feat: add Braintree gateway bwh_payments#23, Settings → Payments lists all six gateways from get_gateways(), and PayPal and Braintree are the ones with reusable_checkout.
  • On a site with feat: add PayPal gateway bwh_payments#7, a USD cart offers PayPal and the same cart in INR doesn't.
  • PayPal Sandbox with a real webhook:
    • Paying creates the Sales Order, Sales Invoice and Payment Entry
    • The return page and the webhook arriving together still produce one order
    • With the browser closed after payment, the webhook alone places the order
    • A partial refund, then the balance, both go through

Dependency

Needs bwhtech/bwh_payments#7 (api.get_gateways() and PayPal). Braintree needs bwhtech/bwh_payments#23.

IMG_5680.MOV
image

@ishad05
ishad05 force-pushed the feat/paypal-commera branch from f3f26fc to c82fe8f Compare September 29, 2026 17:23
@ishad05
ishad05 marked this pull request as ready for review September 29, 2026 18:00
@NagariaHussain

Copy link
Copy Markdown
Contributor

No gateway specific code should go in commera, it should be a plugin, and commera should provide necessary hooks.

@Rl0007

ishad05 and others added 3 commits October 11, 2026 17:11
/en-US/ and /en-GB/ redirected to /en/ without the query string, so a
shopper whose browser language is US or UK English came back from the
gateway to a confirmation page with no reference_id and saw "could not
confirm". The redirects a return URL can hit now forward the query.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A gateway webhook runs as Administrator, so place_order stamped
Administrator as the Sales Order owner and the order never appeared in
the shopper's account. The shopper is now read from the cart's
contact_email, which the storefront sets to their login, whenever the
order is placed as Administrator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Commera no longer names a gateway outside its built-in registry. Provider
apps register a card through the commera_payment_gateways hook (spec 01
§3.1), and Commera acts on the card's capabilities: with
reusable_checkout, a returning shopper is sent back to the still-open
checkout link and the cart is kept while that payment is pending. This
replaces the PayPal-specific checkout reuse and cart cleanup check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ishad05 ishad05 mentioned this pull request Oct 11, 2026
7 tasks done
ishad05 and others added 4 commits October 11, 2026 17:48
Conflicts:
- get_order_shopper: develop's version (guest carts) plus the
  Administrator case, since bwh_payments places webhook orders as
  Administrator, not Guest.
- checkout page: develop's get_checkout_payment_methods, now starting
  from the gateways that can take the cart's currency.
- jobs.py imports and test_payment_hooks: both sides kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
develop's plugin system reads keyed handlers from commera_hooks, checks
each app's commera_api_version, and warns about unknown keys. Payment
gateways now use that: a payment_gateways key in commera_hooks (added to
HANDLER_HOOKS) instead of a separate top-level hook.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pper

bwh_payments applies a webhook's payment as Administrator, so a gateway
order placed that way belongs to the cart's shopper. A COD order is
placed by whoever is signed in, as on develop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A gateway card is a registration, like commera_storefront_blocks, not a
checkout handler that takes the cart like the commera_hooks keys. It moves
to its own top-level hook, as the extensibility spec names it, and keeps
the commera_api_version check through is_supported_app.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ishad05
ishad05 marked this pull request as draft October 11, 2026 12:40
- A provider app's card that raises or lacks slug, label or
  settings_doctype is logged and skipped, like develop's other plugin
  hooks, instead of failing every checkout, the cart cleanup and the
  payments settings.
- Reusing an open checkout link first runs the checks a new checkout
  runs: the gateway must still be offered after plugin payment_methods
  filters, and validate_cart must pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ishad05
ishad05 marked this pull request as ready for review October 12, 2026 05:36
ishad05 and others added 2 commits October 12, 2026 12:23
bwh_payments v1-stable provides the per-currency gateway list as
api.get_gateway_options, built on each gateway's supported_currencies().
Commera reads that instead of a utils helper that no longer exists.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Settings → Payments kept its own list of Razorpay, Stripe, Telr and Tabby,
and bwh_payments registered PayPal and Braintree through
commera_payment_gateways, so a gateway had to be named in Commera or bwh_payments
had to know about Commera.

Cards now come from bwh_payments.api.get_gateways(): every gateway it
ships, with its description, docs link, settings DocType and capabilities.
commera_payment_gateways stays for provider apps outside bwh_payments.
Cards are listed in bwh_payments' order (by name).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ishad05
ishad05 marked this pull request as draft October 14, 2026 09:50
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.

3 participants