Skip to content

feat: onramper headless native buy(OK-58151) - #12875

Draft
weatherstar wants to merge 22 commits into
xfrom
feat/onramper-headless-buy
Draft

feat: onramper headless native buy(OK-58151)#12875
weatherstar wants to merge 22 commits into
xfrom
feat/onramper-headless-buy

Conversation

@weatherstar

Copy link
Copy Markdown
Contributor

Summary

  • Native in-app buy flow (iOS) built on the Onramper headless SDK 1.1.1: amount keypad with presets and live quotes, review screen with order breakdown and the SDK's native Apple Pay button, and a result page — replacing the web widget on supported tokens.
  • Entry is gated by the backend headlessSupported flag per token (OK-58060); anything unsupported, non-iOS, or missing credentials falls back to the existing web widget (fail-closed).
  • Production credentials are injected at build time from CI secrets (OK-59538); dev builds use staging credentials and the Onramper development environment.

Intent & Context

Buying crypto currently opens Onramper's hosted web widget. This PR ships the native "headless" purchase experience (quote → review → Apple Pay → result) inside the app for a much shorter, native-feeling path. Scope is deliberately iOS-only buy; sell and all unsupported cases stay on the web widget. i18n of the new copy is owned by product and out of scope (strings are English for now).

Design Decisions

  • Fail-closed everywhere: no backend flag, no onramperNetworkCode, no account address, no production credentials, or Android → web widget / mock. A real checkout is never quoted against an empty destination address.
  • Server-driven coverage: provider/network coverage comes from the backend flag and server-delivered Onramper network slugs, not a client allowlist. Country comes from IP geo-detect (matches what providers enforce at checkout).
  • Error classification: structural SDK errors (attestation, configuration, device blocked, …) route to the web fallback; transient ones surface inline with retry. Nitro bridge drops structured error payloads on throws, so the real error code is recovered from the Swift enum description string.
  • Single-consume native button: the SDK's prepared pay button cannot be remounted; leaving review re-quotes so the next entry gets a fresh button. Checkout reset() and client teardown are deferred out of navigation-transition windows to avoid device-observed hard freezes.
  • Honest result copy: the SDK's completed event only means the order was handed to the provider — charge and delivery settle asynchronously and can still fail (observed in production 2026-08-13: Apple Pay authorized, completed fired, yet no charge and no crypto). The result page therefore says "Order submitted", and the full order ID is copyable for support lookups.
  • App Attest environment is asserted per build configuration (Release must be production); a build-phase check fails the build otherwise.

Changes Detail

  • packages/kit/src/views/FiatCrypto/pages/HeadlessBuy/ — amount input (custom keypad, presets, live estimate), review mode with quote breakdown and native pay button, token selector, result page.
  • packages/kit/src/views/FiatCrypto/components/Headless/ — checkout lifecycle hook (useOnramperCheckout: session mint, debounced re-quote, event wiring, no-flicker button swap), action zone, info card, error copy mapping.
  • packages/shared/src/modules3rdParty/onramper/ — SDK adapter (real client on iOS, mock elsewhere), credentials/env selection, error-code recovery; hardware entry points untouched.
  • packages/kit-bg / fiat-crypto service — Onramper session mint endpoint wiring and headless-support token gating.
  • Funnel analytics (fiatCrypto.onramper scope) — enter/quote/completed/failed/web-fallback events, checkoutId logged to server for order tracing.
  • Native/build: @onramper/onramper-react-native 1.1.1, react-native-nitro-modules 0.35.2 with regenerated codegen patches for all 21 nitro packages (Android Kotlin compatibility; can be dropped once upstream ships 0.35-based packages), iOS deployment target 15.5 → 16.0 (SDK hard requirement, approved by huhuanming), App Attest entitlement per configuration.

Risk Assessment

  • Risk Level: Medium
  • Affected Platforms: Mobile (iOS feature; Android/desktop/web/ext keep existing behavior via fallbacks, but the nitro 0.35 patches and deployment-target bump affect the whole mobile app)
  • Risk Areas: nitro codegen patches must stay in lockstep with package versions when x moves; App Attest environment on EAS-built profiles; real-money path — production checkout settlement is still under joint investigation with Onramper (order can complete client-side yet fail asynchronously provider-side)

Test plan

  • Staging end-to-end purchase on device (Coinbase sandbox — authorizes without settling)
  • Production quote path verified on device (credentials + App Attest chain)
  • Android bundleGoogleRelease and iOS Release compile with regenerated nitro patches
  • Production purchase acceptance (blocked on the settlement investigation with Onramper — order a02871ad…)
  • Device QA pass (entries, quote errors, provider switch, sign-out, web fallback)

franco-chan and others added 20 commits July 17, 2026 16:34
…-sdk-7baf7b

# Conflicts:
#	packages/kit/src/views/AssetDetails/pages/TokenDetails/ActionBuy.tsx
#	packages/kit/src/views/Send/components/SendAutoSizeAmountInput/index.tsx
# Conflicts:
#	packages/shared/src/routes/gallery.ts
- Android real devices get the mock client: creating the real client on
  Android throws synchronously during render (no native module; reachable
  via the dev Gallery which bypasses canUseHeadless)
- Never quote a real checkout against an empty destination address: gate
  entry on accountId at tryOpenHeadlessBuy and hold the quote loop at
  Preparing until the address lookup resolves (mock stays exempt so the
  Simulator/Gallery preview keeps working)
- HeadlessBuy token selector applies the same eligibility as the entry
  gate (headlessSupported AND onramperNetworkCode)
- New Xcode build phase asserts ONEKEY_APPATTEST_ENVIRONMENT=production
  on Release builds, turning the entitlement/credentials consistency rule
  into a build-time failure instead of a doc reminder
Align the OneKeyWallet and ServiceExtension targets with the Podfile
platform (Podfile.properties.json), which the Onramper Headless SDK's
pod spec forces to :ios => '16.0'. The previous 15.5/16.0 split compiled
all Pods against a 16.0 floor while the app still installed on iOS
15.x, where constant-folded #available checks could crash.
@socket-security

socket-security Bot commented Aug 14, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatednpm/​@​onekeyfe/​react-native-check-biometric-auth-changed@​3.0.78 ⏵ 3.0.8278 +61006496100
Updatednpm/​@​onekeyfe/​react-native-perf-memory@​3.0.78 ⏵ 3.0.8277 +81006496100
Updatednpm/​@​onekeyfe/​react-native-native-logger@​3.0.78 ⏵ 3.0.8277 +81006496100
Updatednpm/​@​onekeyfe/​react-native-keychain-module@​3.0.78 ⏵ 3.0.8279 +51006596100
Updatednpm/​@​onekeyfe/​react-native-app-update@​3.0.78 ⏵ 3.0.8278 +81006596100
Addednpm/​@​onekeyfe/​react-native-scroll-guard@​3.0.82791006696100
Updatednpm/​@​onekeyfe/​react-native-device-utils@​3.0.78 ⏵ 3.0.8279 +51006696100
Updatednpm/​@​onekeyfe/​react-native-cloud-kit-module@​3.0.78 ⏵ 3.0.8279 +51006696100
Updatednpm/​@​onekeyfe/​react-native-bundle-update@​3.0.78 ⏵ 3.0.8279 +71006796100
Updatednpm/​@​onekeyfe/​react-native-auto-size-input@​3.0.78 ⏵ 3.0.8279 +710067 +196100
Updatednpm/​react-native-mmkv@​4.1.0 ⏵ 4.2.0100 +110074 +189 -3100
Updatednpm/​@​onekeyfe/​react-native-splash-screen@​3.0.78 ⏵ 3.0.8277 +71007696100
Addednpm/​@​onramper/​onramper-react-native@​1.1.1761001009380
Updatednpm/​@​onekeyfe/​react-native-zip-archive@​3.0.78 ⏵ 3.0.8278 +61007696100
Updatednpm/​@​onekeyfe/​react-native-bundle-crypto@​3.0.78 ⏵ 3.0.8277 +61008196 +1100
Updatednpm/​@​onekeyfe/​react-native-get-random-values@​3.0.78 ⏵ 3.0.8280 +51007896100
Addednpm/​@​onekeyfe/​react-native-segment-slider@​3.0.82791009396100
Updatednpm/​@​onekeyfe/​react-native-skeleton@​3.0.78 ⏵ 3.0.8279 +51008096100
Updatednpm/​@​onekeyfe/​react-native-chart-webview@​3.0.78 ⏵ 3.0.8279 +51008396 +1100
Updatednpm/​@​onekeyfe/​react-native-range-downloader@​3.0.78 ⏵ 3.0.8279 +810091 +196 +1100
Updatednpm/​@​onekeyfe/​react-native-perp-depth-bar@​3.0.78 ⏵ 3.0.8279 +51008996 +1100
Updatednpm/​@​onekeyfe/​react-native-perf-stats@​3.0.78 ⏵ 3.0.8280 +61009296 +1100
Updatednpm/​react-native-nitro-modules@​0.33.2 ⏵ 0.35.2100 +1100100 +195100

View full report

…58151)

Upstream 4.2.0 ships nitrogen 0.35 generated code, so the local
patch-package regeneration of the 4.1.0 codegen is no longer needed.
…egen patches (OK-58151)

3.0.82 ships nitrogen 0.35.2 generated code (published from app-modules
PR #83), so the 20 patch-package patches that regenerated codegen inside
node_modules are no longer needed. The auto-size-input functional patch
(mostRecentEventCount sync) is upstreamed in the same release. Non-nitro
@onekeyfe packages stay on 3.0.78 deliberately.

Verified: published tarballs match the locally regenerated output
byte-for-byte, patch-package applies cleanly, agent:check commit profile
passes, Android bundleGoogleRelease succeeds (Sentry upload skipped
locally), iOS pods resolve (NitroMmkv 4.2.0 / MMKVCore 2.3.0).
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