Skip to content

usePaymentCheckout changes its state when a payment started elsewhere is already in flight #31

Open

Description

@jkasprzyk17

Follow-up to #8 / #29.

Problem

After #29, only one payment can be in flight at a time. usePaymentCheckout and HybridPaymentHandler share one native PaymentHandler, and its guard (iOS paymentCompletion, Android PendingPayment) resolves an overlapping call with "A payment is already in progress". No second sheet opens.

The hook's own guard (the isPaymentInFlight ref) only covers payments started by that hook instance. If the payment in flight was started by HybridPaymentHandler.startPayment() or by another usePaymentCheckout, the hook only finds out from the native result. By then it has already:

  1. set isProcessing to true and cleared result and error,
  2. called native, which rejects the call,
  3. stored the in-progress result in result and error, then set isProcessing back to false.

So a call that never started a payment still replaces the hook's previous result/error and flashes isProcessing. This is documented in docs/docs/api/use-payment-checkout.md, but the hook would be easier to use if a rejected call left its state alone.

Raised by CodeRabbit on #29: #29 (comment)

Where

  • package/src/hooks/usePaymentCheckout.ts (startPayment)
  • package/src/specs/PaymentHandler.nitro.ts
  • package/ios/HybridPaymentHandler.swift
  • package/android/src/main/java/com/margelo/nitro/pay/HybridPaymentHandler.kt, PendingPayment.kt

Suggested fix

  1. Add a synchronous isPaymentInProgress(): boolean to the PaymentHandler spec. Back it with the existing native state: paymentCompletion != nil on iOS, PendingPayment on Android.
  2. In the hook, check it before touching any state. If it returns true, return the in-progress result without calling setIsProcessing, setResult or setError.
  3. Keep the native guard as the source of truth. The check only decides whether the hook updates its state.

A JS-only module-level claim is not enough. It would cover hook vs hook, but not direct HybridPaymentHandler.startPayment() calls, because that export is the raw hybrid object.

#30 (onAuthorize) also changes the spec and the Android handler, so coordinate with it.

Acceptance criteria

  • With a payment in flight from HybridPaymentHandler.startPayment(), the hook's startPayment() returns the in-progress result and leaves isProcessing, result and error unchanged.
  • The same holds when the payment in flight was started by another usePaymentCheckout.
  • The native guard from fix: guard concurrent startPayment calls on Android and in the hook #29 behaves as before.
  • docs/docs/api/use-payment-checkout.md describes the new behaviour.

Testing

  • Jest: change "records the native in-progress result when a direct payment is in flight" in package/src/__tests__/paymentHandler.test.ts to expect unchanged hook state, and add a hook-vs-hook case.
  • JUnit: the Android getter reflects PendingPayment.
  • Manual: overlapping calls on iOS and Android.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions