Easy Stripe Component

Here is an easy Stripe component for Moqui: GitHub - coarchy/stripe: Stripe Component for Moqui based on Coarchy code written by Michael Jones

I’ve been working on this as part of another project. I’ve tried to make it as easy as possible to use. If you have any questions or feature requests let me know

What you need to know:

Usage

Note: Can be used with a party logged in or from a guest checkout perspective properly.

  • Create a SalesOrder with a Product with a valid ProductStore and ProductPrice
    call stripe.StripeServices.create#CheckoutFromSalesOrder with the order, and your successUrl and cancelUrl
    Optionally set the setup_future_usage to "off_session" to allow for charging customer on their payment method (like in a subscription or usage based model)

  • Redirect the user to the checkoutUrl returned from the service (note the checkoutUrl and checkoutId are added to the OrderPart that is passed in)

  • If this is the first time the user has checkout out (or they’re in not logged in), they will enter their payment information like name, credit card(depending on payment methods configurd in stripe), and billing address.

  • If the user is already logged in or the orderPart.customerPartyId is set and that party has a valid PartyIdentification with type PtidSci and a valid payment method in stripe, the user will only need to type in new information if they want to change it.

  • The user will then submit the payment in the Stripe Checkout screen and will redirect to your successUrl.

  • You will then need to deliver the Product / Service to the User who just paid (this can be done through checking the status of the OrderPart and if the OrderPart is in the OrderPlaced or later status then it should be okay to deliver the product to the OrderPart.customerPartyId )

  • At any time, if the customer wants to cancel the order part, call stripe.StripeServices.cancel#CheckoutFromSalesOrderPart

  • If the User is in the Stripe Checkout screen, Stripe should redirect to your customer to cancelUrl where they can restart their Order, leave feedback, or whatever

You are responsible for:

Things to consider doing:

  • Configure the Stripe Checkout branding settings: production test
  • Configure the Handling of the stripe webhooks. Do this by:
    • Creating a seca for the service before or after the stripe service potential services:
      • Webhook checkout.session.completed: stripe.StripeServices.consume#CheckoutSessionCompleted
      • Webhook checkout.session.expired: stripe.StripeServices.consume#CheckoutSessionExpired
      • Service: stripe.StripeServices.create#CheckoutFromSalesOrder
      • Service: stripe.StripeServices.cancel#CheckoutFromSalesOrderPart
    • Overriding the services by creating a file in compone://yourcomponent/service/stripe/StripeServices.xml with the same name as the service overriding

Things not considered:

  • Moqui Refunds (message me here for implementation: Profile - michael - Moqui Forum)
  • Moqui Vendor Rejection seca (message me here for implementation: Profile - michael - Moqui Forum)
  • Stripe Taxes
  • Stripe Subscriptions
  • Stripe Customer Portal
  • Stripe Connect
  • Stripe Invoices
  • Stripe Invoice Emails
  • Stripe Receipt Emails

Limitations

  • stripe is a big set of products, but we only use a very tiny subset of them here
  • at the moment no vaulting functionality for a customer service agent entering information on behalf of a customer
3 Likes

Hi Fellas! My very-clever-AI-agent-a-bit-lazy :wink: write this post and asked me if I could paste it in here.

[AGENT STARTING]
Moqui 4.0.0, Jetty 12.1.5, using the coarchy/stripe component.

When Stripe returns a 401 Unauthorized (invalid or missing API key), Moqui throws:

400: BAD_QUOTES_IN_TOKEN

Root cause: Stripe includes WWW-Authenticate: Bearer realm="api.stripe.com" in its 4xx responses. Jetty 12’s HTTP client rejects the quoted realm value as non-compliant with RFC 7230 strict mode — this happens on the client side, during response parsing, not on the server side.

The net effect is that the actual Stripe error (401 Invalid API Key) is masked by a Jetty parsing exception, which makes the root cause harder to diagnose.

Worth noting: coarchy/stripe already sidesteps this by using java.net.http.HttpClient (Java 11+) internally rather than Moqui’s RestClient. So if you’re using that component, you won’t hit this in normal operation — you’ll only see it if something causes a 401 (wrong key, expired key, etc.), and even then the Jetty error surfaces instead of the clean Stripe message.

However, anyone building a custom Stripe integration (or any integration with an API that returns quoted WWW-Authenticate headers) using ec.service.rest() / RestClient directly will hit this with no obvious way around it.

Question: Is there a supported way to configure HttpCompliance.RFC7230_LEGACY (or equivalent lenient mode) for Moqui’s RestClient without patching moqui-framework itself? From reading the source, RestClient constructs the Jetty HttpClient internally with no compliance-mode hook exposed.

Or is this worth raising as a framework-level issue — exposing HTTP compliance configuration as an option in RestClient?
[AGENT FINISH]

P.S. I’m mostly using Hermes Agent with Claude Sonnet 4.6 and eventually Claude Opus 4.6.

@michael what is the flow for this use case? Do you create a subscription product and process a sales order? Is it only for purposes of capturing the payment method or do you collect pre-payment?

It uses the typical Moqui model of Product, ProductStore, order, and ProductPrice to process a sales order. It allows for signing up for subscriptions or a one time purchase of a product through the stripe checkout product.

The flow is given a custom ecommerce checkout for a product with a price configured for stripe, generate a stripe checkout link for payment.