stripe-react-native shipping details: unblocking a failing API surface check

A fix for missing carrier, phone and tracking number in stripe-react-native shipping details was stuck on a failing CI check. The remaining diff was not in the pull request at all, and the type still accepted a field that the native SDKs drop.

Category
Code review
Published
Reading time
2 min read

The problem

Issue #1959, open since May 2025: when an app passes shippingDetails to confirmPayment, the name and address reach Stripe, but carrier and phone do not. In a React Native bridge, the native side only reads the fields it maps explicitly; everything else is ignored without an error. Both native mappers built the shipping parameters from name and address only, and on the TypeScript side ShippingDetails was simply BillingDetails, which has no carrier or trackingNumber at all.

The proposed fix

The pull request maps the three fields on both platforms and widens the type:

android/.../utils/Mappers.kt
return ConfirmPaymentIntentParams.Shipping(
  name = getValOr(shippingDetails, "name") ?: "",
  address = address,
  carrier = getValOr(shippingDetails, "carrier"),
  phone = getValOr(shippingDetails, "phone"),
  trackingNumber = getValOr(shippingDetails, "trackingNumber"),
)
src/types/PaymentMethod.ts
export type ShippingDetails = BillingDetails & {
  carrier?: string;
  trackingNumber?: string;
};

A maintainer had approved it, then asked for the build to be fixed before merging, and a day later noted that "the same tasks continue to fail".

What I found in review

The failing check was not about this change

The repository runs API Extractor, which generates a report of the public TypeScript surface; CI fails when the committed report differs from the regenerated one. I read the failed job log. The only remaining difference was one removed // (undocumented) comment above style?: StyleProp<ViewStyle> in the platform pay button props, around line 3568 of the report. The new ShippingDetails entry already matched. That kind of drift comes from main, not from the pull request, so rebasing and regenerating the report (yarn api-extractor run --local) should clear it.

The type is still wider than the native objects

BillingDetails includes email. Stripe's shipping object has no email, and neither do ConfirmPaymentIntentParams.Shipping on Android nor STPPaymentIntentShippingDetailsParams on iOS. So an app can pass shippingDetails.email, see no error, and lose it. That is the same class of bug the pull request fixes, one field over.

TypeScript
export type ShippingDetails = Omit<BillingDetails, 'email'> & {
  carrier?: string;
  trackingNumber?: string;
};

With Omit, an object literal containing email becomes a compile error. I marked it as arguably out of scope.

What happened next

Takeaways

  • When CI fails on a generated report, read the diff in the log before changing code. Here the change was correct and the report was stale.
  • Across a native bridge, the TypeScript type is the only guard. If it is wider than what the native side reads, data disappears silently.
  • Small, specific help ("the remaining difference is this one line, regenerate it this way") is often what moves a stuck contribution.

References

Code and history behind this post

More

Theme